/*!
 * vue-router v4.0.15
 * (c) 2022 Eduardo San Martin Morote
 * @license MIT
 */
var VueRouter = (function(exports, vue) {
	'use strict';

	const hasSymbol = typeof Symbol === 'function' && typeof Symbol.toStringTag === 'symbol';
	const PolySymbol = (name) =>
		// vr = vue router
		hasSymbol ?
		Symbol('[vue-router]: ' + name) :
		('[vue-router]: ') + name;
	// rvlm = Router View Location Matched
	/**
	 * RouteRecord being rendered by the closest ancestor Router View. Used for
	 * `onBeforeRouteUpdate` and `onBeforeRouteLeave`. rvlm stands for Router View
	 * Location Matched
	 *
	 * @internal
	 */
	const matchedRouteKey = /*#__PURE__*/ PolySymbol('router view location matched');
	/**
	 * Allows overriding the router view depth to control which component in
	 * `matched` is rendered. rvd stands for Router View Depth
	 *
	 * @internal
	 */
	const viewDepthKey = /*#__PURE__*/ PolySymbol('router view depth');
	/**
	 * Allows overriding the router instance returned by `useRouter` in tests. r
	 * stands for router
	 *
	 * @internal
	 */
	const routerKey = /*#__PURE__*/ PolySymbol('router');
	/**
	 * Allows overriding the current route returned by `useRoute` in tests. rl
	 * stands for route location
	 *
	 * @internal
	 */
	const routeLocationKey = /*#__PURE__*/ PolySymbol('route location');
	/**
	 * Allows overriding the current route used by router-view. Internally this is
	 * used when the `route` prop is passed.
	 *
	 * @internal
	 */
	const routerViewLocationKey = /*#__PURE__*/ PolySymbol('router view location');

	const isBrowser = typeof window !== 'undefined';

	function isESModule(obj) {
		return obj.__esModule || (hasSymbol && obj[Symbol.toStringTag] === 'Module');
	}
	const assign = Object.assign;

	function applyToParams(fn, params) {
		const newParams = {};
		for (const key in params) {
			const value = params[key];
			newParams[key] = Array.isArray(value) ? value.map(fn) : fn(value);
		}
		return newParams;
	}
	const noop = () => {};

	function warn(msg) {
		// avoid using ...args as it breaks in older Edge builds
		const args = Array.from(arguments).slice(1);
		console.warn.apply(console, ['[Vue Router warn]: ' + msg].concat(args));
	}

	const TRAILING_SLASH_RE = /\/$/;
	const removeTrailingSlash = (path) => path.replace(TRAILING_SLASH_RE, '');
	/**
	 * Transforms an URI into a normalized history location
	 *
	 * @param parseQuery
	 * @param location - URI to normalize
	 * @param currentLocation - current absolute location. Allows resolving relative
	 * paths. Must start with `/`. Defaults to `/`
	 * @returns a normalized history location
	 */
	function parseURL(parseQuery, location, currentLocation = '/') {
		let path, query = {},
			searchString = '',
			hash = '';
		// Could use URL and URLSearchParams but IE 11 doesn't support it
		const searchPos = location.indexOf('?');
		const hashPos = location.indexOf('#', searchPos > -1 ? searchPos : 0);
		if (searchPos > -1) {
			path = location.slice(0, searchPos);
			searchString = location.slice(searchPos + 1, hashPos > -1 ? hashPos : location.length);
			query = parseQuery(searchString);
		}
		if (hashPos > -1) {
			path = path || location.slice(0, hashPos);
			// keep the # character
			hash = location.slice(hashPos, location.length);
		}
		// no search and no query
		path = resolveRelativePath(path != null ? path : location, currentLocation);
		// empty path means a relative query or hash `?foo=f`, `#thing`
		return {
			fullPath: path + (searchString && '?') + searchString + hash,
			path,
			query,
			hash,
		};
	}
	/**
	 * Stringifies a URL object
	 *
	 * @param stringifyQuery
	 * @param location
	 */
	function stringifyURL(stringifyQuery, location) {
		const query = location.query ? stringifyQuery(location.query) : '';
		return location.path + (query && '?') + query + (location.hash || '');
	}
	/**
	 * Strips off the base from the beginning of a location.pathname in a non
	 * case-sensitive way.
	 *
	 * @param pathname - location.pathname
	 * @param base - base to strip off
	 */
	function stripBase(pathname, base) {
		// no base or base is not found at the beginning
		if (!base || !pathname.toLowerCase().startsWith(base.toLowerCase()))
			return pathname;
		return pathname.slice(base.length) || '/';
	}
	/**
	 * Checks if two RouteLocation are equal. This means that both locations are
	 * pointing towards the same {@link RouteRecord} and that all `params`, `query`
	 * parameters and `hash` are the same
	 *
	 * @param a - first {@link RouteLocation}
	 * @param b - second {@link RouteLocation}
	 */
	function isSameRouteLocation(stringifyQuery, a, b) {
		const aLastIndex = a.matched.length - 1;
		const bLastIndex = b.matched.length - 1;
		return (aLastIndex > -1 &&
			aLastIndex === bLastIndex &&
			isSameRouteRecord(a.matched[aLastIndex], b.matched[bLastIndex]) &&
			isSameRouteLocationParams(a.params, b.params) &&
			stringifyQuery(a.query) === stringifyQuery(b.query) &&
			a.hash === b.hash);
	}
	/**
	 * Check if two `RouteRecords` are equal. Takes into account aliases: they are
	 * considered equal to the `RouteRecord` they are aliasing.
	 *
	 * @param a - first {@link RouteRecord}
	 * @param b - second {@link RouteRecord}
	 */
	function isSameRouteRecord(a, b) {
		// since the original record has an undefined value for aliasOf
		// but all aliases point to the original record, this will always compare
		// the original record
		return (a.aliasOf || a) === (b.aliasOf || b);
	}

	function isSameRouteLocationParams(a, b) {
		if (Object.keys(a).length !== Object.keys(b).length)
			return false;
		for (const key in a) {
			if (!isSameRouteLocationParamsValue(a[key], b[key]))
				return false;
		}
		return true;
	}

	function isSameRouteLocationParamsValue(a, b) {
		return Array.isArray(a) ?
			isEquivalentArray(a, b) :
			Array.isArray(b) ?
			isEquivalentArray(b, a) :
			a === b;
	}
	/**
	 * Check if two arrays are the same or if an array with one single entry is the
	 * same as another primitive value. Used to check query and parameters
	 *
	 * @param a - array of values
	 * @param b - array of values or a single value
	 */
	function isEquivalentArray(a, b) {
		return Array.isArray(b) ?
			a.length === b.length && a.every((value, i) => value === b[i]) :
			a.length === 1 && a[0] === b;
	}
	/**
	 * Resolves a relative path that starts with `.`.
	 *
	 * @param to - path location we are resolving
	 * @param from - currentLocation.path, should start with `/`
	 */
	function resolveRelativePath(to, from) {
		if (to.startsWith('/'))
			return to;
		if (!from.startsWith('/')) {
			warn(
				`Cannot resolve a relative location without an absolute path. Trying to resolve "${to}" from "${from}". It should look like "/${from}".`);
			return to;
		}
		if (!to)
			return from;
		const fromSegments = from.split('/');
		const toSegments = to.split('/');
		let position = fromSegments.length - 1;
		let toPosition;
		let segment;
		for (toPosition = 0; toPosition < toSegments.length; toPosition++) {
			segment = toSegments[toPosition];
			// can't go below zero
			if (position === 1 || segment === '.')
				continue;
			if (segment === '..')
				position--;
			// found something that is not relative path
			else
				break;
		}
		return (fromSegments.slice(0, position).join('/') +
			'/' +
			toSegments
			.slice(toPosition - (toPosition === toSegments.length ? 1 : 0))
			.join('/'));
	}

	var NavigationType;
	(function(NavigationType) {
		NavigationType["pop"] = "pop";
		NavigationType["push"] = "push";
	})(NavigationType || (NavigationType = {}));
	var NavigationDirection;
	(function(NavigationDirection) {
		NavigationDirection["back"] = "back";
		NavigationDirection["forward"] = "forward";
		NavigationDirection["unknown"] = "";
	})(NavigationDirection || (NavigationDirection = {}));
	/**
	 * Starting location for Histories
	 */
	const START = '';
	// Generic utils
	/**
	 * Normalizes a base by removing any trailing slash and reading the base tag if
	 * present.
	 *
	 * @param base - base to normalize
	 */
	function normalizeBase(base) {
		if (!base) {
			if (isBrowser) {
				// respect <base> tag
				const baseEl = document.querySelector('base');
				base = (baseEl && baseEl.getAttribute('href')) || '/';
				// strip full URL origin
				base = base.replace(/^\w+:\/\/[^\/]+/, '');
			} else {
				base = '/';
			}
		}
		// ensure leading slash when it was removed by the regex above avoid leading
		// slash with hash because the file could be read from the disk like file://
		// and the leading slash would cause problems
		if (base[0] !== '/' && base[0] !== '#')
			base = '/' + base;
		// remove the trailing slash so all other method can just do `base + fullPath`
		// to build an href
		return removeTrailingSlash(base);
	}
	// remove any character before the hash
	const BEFORE_HASH_RE = /^[^#]+#/;

	function createHref(base, location) {
		return base.replace(BEFORE_HASH_RE, '#') + location;
	}

	function getElementPosition(el, offset) {
		const docRect = document.documentElement.getBoundingClientRect();
		const elRect = el.getBoundingClientRect();
		return {
			behavior: offset.behavior,
			left: elRect.left - docRect.left - (offset.left || 0),
			top: elRect.top - docRect.top - (offset.top || 0),
		};
	}
	const computeScrollPosition = () => ({
		left: window.pageXOffset,
		top: window.pageYOffset,
	});

	function scrollToPosition(position) {
		let scrollToOptions;
		if ('el' in position) {
			const positionEl = position.el;
			const isIdSelector = typeof positionEl === 'string' && positionEl.startsWith('#');
			/**
			 * `id`s can accept pretty much any characters, including CSS combinators
			 * like `>` or `~`. It's still possible to retrieve elements using
			 * `document.getElementById('~')` but it needs to be escaped when using
			 * `document.querySelector('#\\~')` for it to be valid. The only
			 * requirements for `id`s are them to be unique on the page and to not be
			 * empty (`id=""`). Because of that, when passing an id selector, it should
			 * be properly escaped for it to work with `querySelector`. We could check
			 * for the id selector to be simple (no CSS combinators `+ >~`) but that
			 * would make things inconsistent since they are valid characters for an
			 * `id` but would need to be escaped when using `querySelector`, breaking
			 * their usage and ending up in no selector returned. Selectors need to be
			 * escaped:
			 *
			 * - `#1-thing` becomes `#\31 -thing`
			 * - `#with~symbols` becomes `#with\\~symbols`
			 *
			 * - More information about  the topic can be found at
			 *   https://mathiasbynens.be/notes/html5-id-class.
			 * - Practical example: https://mathiasbynens.be/demo/html5-id
			 */
			if (typeof position.el === 'string') {
				if (!isIdSelector || !document.getElementById(position.el.slice(1))) {
					try {
						const foundEl = document.querySelector(position.el);
						if (isIdSelector && foundEl) {
							warn(
								`The selector "${position.el}" should be passed as "el: document.querySelector('${position.el}')" because it starts with "#".`);
							// return to avoid other warnings
							return;
						}
					} catch (err) {
						warn(
							`The selector "${position.el}" is invalid. If you are using an id selector, make sure to escape it. You can find more information about escaping characters in selectors at https://mathiasbynens.be/notes/css-escapes or use CSS.escape (https://developer.mozilla.org/en-US/docs/Web/API/CSS/escape).`);
						// return to avoid other warnings
						return;
					}
				}
			}
			const el = typeof positionEl === 'string' ?
				isIdSelector ?
				document.getElementById(positionEl.slice(1)) :
				document.querySelector(positionEl) :
				positionEl;
			if (!el) {
				warn(`Couldn't find element using selector "${position.el}" returned by scrollBehavior.`);
				return;
			}
			scrollToOptions = getElementPosition(el, position);
		} else {
			scrollToOptions = position;
		}
		if ('scrollBehavior' in document.documentElement.style)
			window.scrollTo(scrollToOptions);
		else {
			window.scrollTo(scrollToOptions.left != null ? scrollToOptions.left : window.pageXOffset,
				scrollToOptions.top != null ? scrollToOptions.top : window.pageYOffset);
		}
	}

	function getScrollKey(path, delta) {
		const position = history.state ? history.state.position - delta : -1;
		return position + path;
	}
	const scrollPositions = new Map();

	function saveScrollPosition(key, scrollPosition) {
		scrollPositions.set(key, scrollPosition);
	}

	function getSavedScrollPosition(key) {
		const scroll = scrollPositions.get(key);
		// consume it so it's not used again
		scrollPositions.delete(key);
		return scroll;
	}
	// TODO: RFC about how to save scroll position
	/**
	 * ScrollBehavior instance used by the router to compute and restore the scroll
	 * position when navigating.
	 */
	// export interface ScrollHandler<ScrollPositionEntry extends HistoryStateValue, ScrollPosition extends ScrollPositionEntry> {
	//   // returns a scroll position that can be saved in history
	//   compute(): ScrollPositionEntry
	//   // can take an extended ScrollPositionEntry
	//   scroll(position: ScrollPosition): void
	// }
	// export const scrollHandler: ScrollHandler<ScrollPosition> = {
	//   compute: computeScroll,
	//   scroll: scrollToPosition,
	// }

	let createBaseLocation = () => location.protocol + '//' + location.host;
	/**
	 * Creates a normalized history location from a window.location object
	 * @param location -
	 */
	function createCurrentLocation(base, location) {
		const {
			pathname,
			search,
			hash
		} = location;
		// allows hash bases like #, /#, #/, #!, #!/, /#!/, or even /folder#end
		const hashPos = base.indexOf('#');
		if (hashPos > -1) {
			let slicePos = hash.includes(base.slice(hashPos)) ?
				base.slice(hashPos).length :
				1;
			let pathFromHash = hash.slice(slicePos);
			// prepend the starting slash to hash so the url starts with /#
			if (pathFromHash[0] !== '/')
				pathFromHash = '/' + pathFromHash;
			return stripBase(pathFromHash, '');
		}
		const path = stripBase(pathname, base);
		return path + search + hash;
	}

	function useHistoryListeners(base, historyState, currentLocation, replace) {
		let listeners = [];
		let teardowns = [];
		// TODO: should it be a stack? a Dict. Check if the popstate listener
		// can trigger twice
		let pauseState = null;
		const popStateHandler = ({
			state,
		}) => {
			const to = createCurrentLocation(base, location);
			const from = currentLocation.value;
			const fromState = historyState.value;
			let delta = 0;
			if (state) {
				currentLocation.value = to;
				historyState.value = state;
				// ignore the popstate and reset the pauseState
				if (pauseState && pauseState === from) {
					pauseState = null;
					return;
				}
				delta = fromState ? state.position - fromState.position : 0;
			} else {
				replace(to);
			}
			// console.log({ deltaFromCurrent })
			// Here we could also revert the navigation by calling history.go(-delta)
			// this listener will have to be adapted to not trigger again and to wait for the url
			// to be updated before triggering the listeners. Some kind of validation function would also
			// need to be passed to the listeners so the navigation can be accepted
			// call all listeners
			listeners.forEach(listener => {
				listener(currentLocation.value, from, {
					delta,
					type: NavigationType.pop,
					direction: delta ?
						delta > 0 ?
						NavigationDirection.forward :
						NavigationDirection.back :
						NavigationDirection.unknown,
				});
			});
		};

		function pauseListeners() {
			pauseState = currentLocation.value;
		}

		function listen(callback) {
			// setup the listener and prepare teardown callbacks
			listeners.push(callback);
			const teardown = () => {
				const index = listeners.indexOf(callback);
				if (index > -1)
					listeners.splice(index, 1);
			};
			teardowns.push(teardown);
			return teardown;
		}

		function beforeUnloadListener() {
			const {
				history
			} = window;
			if (!history.state)
				return;
			history.replaceState(assign({}, history.state, {
				scroll: computeScrollPosition()
			}), '');
		}

		function destroy() {
			for (const teardown of teardowns)
				teardown();
			teardowns = [];
			window.removeEventListener('popstate', popStateHandler);
			window.removeEventListener('beforeunload', beforeUnloadListener);
		}
		// setup the listeners and prepare teardown callbacks
		window.addEventListener('popstate', popStateHandler);
		window.addEventListener('beforeunload', beforeUnloadListener);
		return {
			pauseListeners,
			listen,
			destroy,
		};
	}
	/**
	 * Creates a state object
	 */
	function buildState(back, current, forward, replaced = false, computeScroll = false) {
		return {
			back,
			current,
			forward,
			replaced,
			position: window.history.length,
			scroll: computeScroll ? computeScrollPosition() : null,
		};
	}

	function useHistoryStateNavigation(base) {
		const {
			history,
			location
		} = window;
		// private variables
		const currentLocation = {
			value: createCurrentLocation(base, location),
		};
		const historyState = {
			value: history.state
		};
		// build current history entry as this is a fresh navigation
		if (!historyState.value) {
			changeLocation(currentLocation.value, {
				back: null,
				current: currentLocation.value,
				forward: null,
				// the length is off by one, we need to decrease it
				position: history.length - 1,
				replaced: true,
				// don't add a scroll as the user may have an anchor and we want
				// scrollBehavior to be triggered without a saved position
				scroll: null,
			}, true);
		}

		function changeLocation(to, state, replace) {
			/**
			 * if a base tag is provided and we are on a normal domain, we have to
			 * respect the provided `base` attribute because pushState() will use it and
			 * potentially erase anything before the `#` like at
			 * https://github.com/vuejs/router/issues/685 where a base of
			 * `/folder/#` but a base of `/` would erase the `/folder/` section. If
			 * there is no host, the `<base>` tag makes no sense and if there isn't a
			 * base tag we can just use everything after the `#`.
			 */
			const hashIndex = base.indexOf('#');
			const url = hashIndex > -1 ?
				(location.host && document.querySelector('base') ?
					base :
					base.slice(hashIndex)) + to :
				createBaseLocation() + base + to;
			try {
				// BROWSER QUIRK
				// NOTE: Safari throws a SecurityError when calling this function 100 times in 30 seconds
				history[replace ? 'replaceState' : 'pushState'](state, '', url);
				historyState.value = state;
			} catch (err) {
				{
					warn('Error with push/replace State', err);
				}
				// Force the navigation, this also resets the call count
				location[replace ? 'replace' : 'assign'](url);
			}
		}

		function replace(to, data) {
			const state = assign({}, history.state, buildState(historyState.value.back,
				// keep back and forward entries but override current position
				to, historyState.value.forward, true), data, {
				position: historyState.value.position
			});
			changeLocation(to, state, true);
			currentLocation.value = to;
		}

		function push(to, data) {
			// Add to current entry the information of where we are going
			// as well as saving the current position
			const currentState = assign({},
				// use current history state to gracefully handle a wrong call to
				// history.replaceState
				// https://github.com/vuejs/router/issues/366
				historyState.value, history.state, {
					forward: to,
					scroll: computeScrollPosition(),
				});
			if (!history.state) {
				warn(`history.state seems to have been manually replaced without preserving the necessary values. Make sure to preserve existing history state if you are manually calling history.replaceState:\n\n` +
					`history.replaceState(history.state, '', url)\n\n` +
					`You can find more information at https://next.router.vuejs.org/guide/migration/#usage-of-history-state.`
					);
			}
			changeLocation(currentState.current, currentState, true);
			const state = assign({}, buildState(currentLocation.value, to, null), {
				position: currentState.position + 1
			}, data);
			changeLocation(to, state, false);
			currentLocation.value = to;
		}
		return {
			location: currentLocation,
			state: historyState,
			push,
			replace,
		};
	}
	/**
	 * Creates an HTML5 history. Most common history for single page applications.
	 *
	 * @param base -
	 */
	function createWebHistory(base) {
		base = normalizeBase(base);
		const historyNavigation = useHistoryStateNavigation(base);
		const historyListeners = useHistoryListeners(base, historyNavigation.state, historyNavigation.location,
			historyNavigation.replace);

		function go(delta, triggerListeners = true) {
			if (!triggerListeners)
				historyListeners.pauseListeners();
			history.go(delta);
		}
		const routerHistory = assign({
			// it's overridden right after
			location: '',
			base,
			go,
			createHref: createHref.bind(null, base),
		}, historyNavigation, historyListeners);
		Object.defineProperty(routerHistory, 'location', {
			enumerable: true,
			get: () => historyNavigation.location.value,
		});
		Object.defineProperty(routerHistory, 'state', {
			enumerable: true,
			get: () => historyNavigation.state.value,
		});
		return routerHistory;
	}

	/**
	 * Creates a in-memory based history. The main purpose of this history is to handle SSR. It starts in a special location that is nowhere.
	 * It's up to the user to replace that location with the starter location by either calling `router.push` or `router.replace`.
	 *
	 * @param base - Base applied to all urls, defaults to '/'
	 * @returns a history object that can be passed to the router constructor
	 */
	function createMemoryHistory(base = '') {
		let listeners = [];
		let queue = [START];
		let position = 0;
		base = normalizeBase(base);

		function setLocation(location) {
			position++;
			if (position === queue.length) {
				// we are at the end, we can simply append a new entry
				queue.push(location);
			} else {
				// we are in the middle, we remove everything from here in the queue
				queue.splice(position);
				queue.push(location);
			}
		}

		function triggerListeners(to, from, {
			direction,
			delta
		}) {
			const info = {
				direction,
				delta,
				type: NavigationType.pop,
			};
			for (const callback of listeners) {
				callback(to, from, info);
			}
		}
		const routerHistory = {
			// rewritten by Object.defineProperty
			location: START,
			// TODO: should be kept in queue
			state: {},
			base,
			createHref: createHref.bind(null, base),
			replace(to) {
				// remove current entry and decrement position
				queue.splice(position--, 1);
				setLocation(to);
			},
			push(to, data) {
				setLocation(to);
			},
			listen(callback) {
				listeners.push(callback);
				return () => {
					const index = listeners.indexOf(callback);
					if (index > -1)
						listeners.splice(index, 1);
				};
			},
			destroy() {
				listeners = [];
				queue = [START];
				position = 0;
			},
			go(delta, shouldTrigger = true) {
				const from = this.location;
				const direction =
					// we are considering delta === 0 going forward, but in abstract mode
					// using 0 for the delta doesn't make sense like it does in html5 where
					// it reloads the page
					delta < 0 ? NavigationDirection.back : NavigationDirection.forward;
				position = Math.max(0, Math.min(position + delta, queue.length - 1));
				if (shouldTrigger) {
					triggerListeners(this.location, from, {
						direction,
						delta,
					});
				}
			},
		};
		Object.defineProperty(routerHistory, 'location', {
			enumerable: true,
			get: () => queue[position],
		});
		return routerHistory;
	}

	/**
	 * Creates a hash history. Useful for web applications with no host (e.g.
	 * `file://`) or when configuring a server to handle any URL is not possible.
	 *
	 * @param base - optional base to provide. Defaults to `location.pathname +
	 * location.search` If there is a `<base>` tag in the `head`, its value will be
	 * ignored in favor of this parameter **but note it affects all the
	 * history.pushState() calls**, meaning that if you use a `<base>` tag, it's
	 * `href` value **has to match this parameter** (ignoring anything after the
	 * `#`).
	 *
	 * @example
	 * ```js
	 * // at https://example.com/folder
	 * createWebHashHistory() // gives a url of `https://example.com/folder#`
	 * createWebHashHistory('/folder/') // gives a url of `https://example.com/folder/#`
	 * // if the `#` is provided in the base, it won't be added by `createWebHashHistory`
	 * createWebHashHistory('/folder/#/app/') // gives a url of `https://example.com/folder/#/app/`
	 * // you should avoid doing this because it changes the original url and breaks copying urls
	 * createWebHashHistory('/other-folder/') // gives a url of `https://example.com/other-folder/#`
	 *
	 * // at file:///usr/etc/folder/index.html
	 * // for locations with no `host`, the base is ignored
	 * createWebHashHistory('/iAmIgnored') // gives a url of `file:///usr/etc/folder/index.html#`
	 * ```
	 */
	function createWebHashHistory(base) {
		// Make sure this implementation is fine in terms of encoding, specially for IE11
		// for `file://`, directly use the pathname and ignore the base
		// location.pathname contains an initial `/` even at the root: `https://example.com`
		base = location.host ? base || location.pathname + location.search : '';
		// allow the user to provide a `#` in the middle: `/base/#/app`
		if (!base.includes('#'))
			base += '#';
		if (!base.endsWith('#/') && !base.endsWith('#')) {
			warn(`A hash base must end with a "#":\n"${base}" should be "${base.replace(/#.*$/, '#')}".`);
		}
		return createWebHistory(base);
	}

	function isRouteLocation(route) {
		return typeof route === 'string' || (route && typeof route === 'object');
	}

	function isRouteName(name) {
		return typeof name === 'string' || typeof name === 'symbol';
	}

	/**
	 * Initial route location where the router is. Can be used in navigation guards
	 * to differentiate the initial navigation.
	 *
	 * @example
	 * ```js
	 * import { START_LOCATION } from 'vue-router'
	 *
	 * router.beforeEach((to, from) => {
	 *   if (from === START_LOCATION) {
	 *     // initial navigation
	 *   }
	 * })
	 * ```
	 */
	const START_LOCATION_NORMALIZED = {
		path: '/',
		name: undefined,
		params: {},
		query: {},
		hash: '',
		fullPath: '/',
		matched: [],
		meta: {},
		redirectedFrom: undefined,
	};

	const NavigationFailureSymbol = /*#__PURE__*/ PolySymbol('navigation failure');
	/**
	 * Enumeration with all possible types for navigation failures. Can be passed to
	 * {@link isNavigationFailure} to check for specific failures.
	 */
	exports.NavigationFailureType = void 0;
	(function(NavigationFailureType) {
		/**
		 * An aborted navigation is a navigation that failed because a navigation
		 * guard returned `false` or called `next(false)`
		 */
		NavigationFailureType[NavigationFailureType["aborted"] = 4] = "aborted";
		/**
		 * A cancelled navigation is a navigation that failed because a more recent
		 * navigation finished started (not necessarily finished).
		 */
		NavigationFailureType[NavigationFailureType["cancelled"] = 8] = "cancelled";
		/**
		 * A duplicated navigation is a navigation that failed because it was
		 * initiated while already being at the exact same location.
		 */
		NavigationFailureType[NavigationFailureType["duplicated"] = 16] = "duplicated";
	})(exports.NavigationFailureType || (exports.NavigationFailureType = {}));
	// DEV only debug messages
	const ErrorTypeMessages = {
		[1 /* MATCHER_NOT_FOUND */ ]({
			location,
			currentLocation
		}) {
			return `No match for\n ${JSON.stringify(location)}${currentLocation
            ? '\nwhile being at\n' + JSON.stringify(currentLocation)
            : ''}`;
		},
		[2 /* NAVIGATION_GUARD_REDIRECT */ ]({
			from,
			to,
		}) {
			return `Redirected from "${from.fullPath}" to "${stringifyRoute(to)}" via a navigation guard.`;
		},
		[4 /* NAVIGATION_ABORTED */ ]({
			from,
			to
		}) {
			return `Navigation aborted from "${from.fullPath}" to "${to.fullPath}" via a navigation guard.`;
		},
		[8 /* NAVIGATION_CANCELLED */ ]({
			from,
			to
		}) {
			return `Navigation cancelled from "${from.fullPath}" to "${to.fullPath}" with a new navigation.`;
		},
		[16 /* NAVIGATION_DUPLICATED */ ]({
			from,
			to
		}) {
			return `Avoided redundant navigation to current location: "${from.fullPath}".`;
		},
	};

	function createRouterError(type, params) {
		// keep full error messages in cjs versions
		{
			return assign(new Error(ErrorTypeMessages[type](params)), {
				type,
				[NavigationFailureSymbol]: true,
			}, params);
		}
	}

	function isNavigationFailure(error, type) {
		return (error instanceof Error &&
			NavigationFailureSymbol in error &&
			(type == null || !!(error.type & type)));
	}
	const propertiesToLog = ['params', 'query', 'hash'];

	function stringifyRoute(to) {
		if (typeof to === 'string')
			return to;
		if ('path' in to)
			return to.path;
		const location = {};
		for (const key of propertiesToLog) {
			if (key in to)
				location[key] = to[key];
		}
		return JSON.stringify(location, null, 2);
	}

	// default pattern for a param: non greedy everything but /
	const BASE_PARAM_PATTERN = '[^/]+?';
	const BASE_PATH_PARSER_OPTIONS = {
		sensitive: false,
		strict: false,
		start: true,
		end: true,
	};
	// Special Regex characters that must be escaped in static tokens
	const REGEX_CHARS_RE = /[.+*?^${}()[\]/\\]/g;
	/**
	 * Creates a path parser from an array of Segments (a segment is an array of Tokens)
	 *
	 * @param segments - array of segments returned by tokenizePath
	 * @param extraOptions - optional options for the regexp
	 * @returns a PathParser
	 */
	function tokensToParser(segments, extraOptions) {
		const options = assign({}, BASE_PATH_PARSER_OPTIONS, extraOptions);
		// the amount of scores is the same as the length of segments except for the root segment "/"
		const score = [];
		// the regexp as a string
		let pattern = options.start ? '^' : '';
		// extracted keys
		const keys = [];
		for (const segment of segments) {
			// the root segment needs special treatment
			const segmentScores = segment.length ? [] : [90 /* Root */ ];
			// allow trailing slash
			if (options.strict && !segment.length)
				pattern += '/';
			for (let tokenIndex = 0; tokenIndex < segment.length; tokenIndex++) {
				const token = segment[tokenIndex];
				// resets the score if we are inside a sub segment /:a-other-:b
				let subSegmentScore = 40 /* Segment */ +
					(options.sensitive ? 0.25 /* BonusCaseSensitive */ : 0);
				if (token.type === 0 /* Static */ ) {
					// prepend the slash if we are starting a new segment
					if (!tokenIndex)
						pattern += '/';
					pattern += token.value.replace(REGEX_CHARS_RE, '\\$&');
					subSegmentScore += 40 /* Static */ ;
				} else if (token.type === 1 /* Param */ ) {
					const {
						value,
						repeatable,
						optional,
						regexp
					} = token;
					keys.push({
						name: value,
						repeatable,
						optional,
					});
					const re = regexp ? regexp : BASE_PARAM_PATTERN;
					// the user provided a custom regexp /:id(\\d+)
					if (re !== BASE_PARAM_PATTERN) {
						subSegmentScore += 10 /* BonusCustomRegExp */ ;
						// make sure the regexp is valid before using it
						try {
							new RegExp(`(${re})`);
						} catch (err) {
							throw new Error(`Invalid custom RegExp for param "${value}" (${re}): ` +
								err.message);
						}
					}
					// when we repeat we must take care of the repeating leading slash
					let subPattern = repeatable ? `((?:${re})(?:/(?:${re}))*)` : `(${re})`;
					// prepend the slash if we are starting a new segment
					if (!tokenIndex)
						subPattern =
						// avoid an optional / if there are more segments e.g. /:p?-static
						// or /:p?-:p2
						optional && segment.length < 2 ?
						`(?:/${subPattern})` :
						'/' + subPattern;
					if (optional)
						subPattern += '?';
					pattern += subPattern;
					subSegmentScore += 20 /* Dynamic */ ;
					if (optional)
						subSegmentScore += -8 /* BonusOptional */ ;
					if (repeatable)
						subSegmentScore += -20 /* BonusRepeatable */ ;
					if (re === '.*')
						subSegmentScore += -50 /* BonusWildcard */ ;
				}
				segmentScores.push(subSegmentScore);
			}
			// an empty array like /home/ -> [[{home}], []]
			// if (!segment.length) pattern += '/'
			score.push(segmentScores);
		}
		// only apply the strict bonus to the last score
		if (options.strict && options.end) {
			const i = score.length - 1;
			score[i][score[i].length - 1] += 0.7000000000000001 /* BonusStrict */ ;
		}
		// TODO: dev only warn double trailing slash
		if (!options.strict)
			pattern += '/?';
		if (options.end)
			pattern += '$';
		// allow paths like /dynamic to only match dynamic or dynamic/... but not dynamic_something_else
		else if (options.strict)
			pattern += '(?:/|$)';
		const re = new RegExp(pattern, options.sensitive ? '' : 'i');

		function parse(path) {
			const match = path.match(re);
			const params = {};
			if (!match)
				return null;
			for (let i = 1; i < match.length; i++) {
				const value = match[i] || '';
				const key = keys[i - 1];
				params[key.name] = value && key.repeatable ? value.split('/') : value;
			}
			return params;
		}

		function stringify(params) {
			let path = '';
			// for optional parameters to allow to be empty
			let avoidDuplicatedSlash = false;
			for (const segment of segments) {
				if (!avoidDuplicatedSlash || !path.endsWith('/'))
					path += '/';
				avoidDuplicatedSlash = false;
				for (const token of segment) {
					if (token.type === 0 /* Static */ ) {
						path += token.value;
					} else if (token.type === 1 /* Param */ ) {
						const {
							value,
							repeatable,
							optional
						} = token;
						const param = value in params ? params[value] : '';
						if (Array.isArray(param) && !repeatable)
							throw new Error(
								`Provided param "${value}" is an array but it is not repeatable (* or + modifiers)`
								);
						const text = Array.isArray(param) ? param.join('/') : param;
						if (!text) {
							if (optional) {
								// if we have more than one optional param like /:a?-static and there are more segments, we don't need to
								// care about the optional param
								if (segment.length < 2 && segments.length > 1) {
									// remove the last slash as we could be at the end
									if (path.endsWith('/'))
										path = path.slice(0, -1);
									// do not append a slash on the next iteration
									else
										avoidDuplicatedSlash = true;
								}
							} else
								throw new Error(`Missing required param "${value}"`);
						}
						path += text;
					}
				}
			}
			return path;
		}
		return {
			re,
			score,
			keys,
			parse,
			stringify,
		};
	}
	/**
	 * Compares an array of numbers as used in PathParser.score and returns a
	 * number. This function can be used to `sort` an array
	 *
	 * @param a - first array of numbers
	 * @param b - second array of numbers
	 * @returns 0 if both are equal, < 0 if a should be sorted first, > 0 if b
	 * should be sorted first
	 */
	function compareScoreArray(a, b) {
		let i = 0;
		while (i < a.length && i < b.length) {
			const diff = b[i] - a[i];
			// only keep going if diff === 0
			if (diff)
				return diff;
			i++;
		}
		// if the last subsegment was Static, the shorter segments should be sorted first
		// otherwise sort the longest segment first
		if (a.length < b.length) {
			return a.length === 1 && a[0] === 40 /* Static */ + 40 /* Segment */ ?
				-1 :
				1;
		} else if (a.length > b.length) {
			return b.length === 1 && b[0] === 40 /* Static */ + 40 /* Segment */ ?
				1 :
				-1;
		}
		return 0;
	}
	/**
	 * Compare function that can be used with `sort` to sort an array of PathParser
	 *
	 * @param a - first PathParser
	 * @param b - second PathParser
	 * @returns 0 if both are equal, < 0 if a should be sorted first, > 0 if b
	 */
	function comparePathParserScore(a, b) {
		let i = 0;
		const aScore = a.score;
		const bScore = b.score;
		while (i < aScore.length && i < bScore.length) {
			const comp = compareScoreArray(aScore[i], bScore[i]);
			// do not return if both are equal
			if (comp)
				return comp;
			i++;
		}
		// if a and b share the same score entries but b has more, sort b first
		return bScore.length - aScore.length;
		// this is the ternary version
		// return aScore.length < bScore.length
		//   ? 1
		//   : aScore.length > bScore.length
		//   ? -1
		//   : 0
	}

	const ROOT_TOKEN = {
		type: 0 /* Static */ ,
		value: '',
	};
	const VALID_PARAM_RE = /[a-zA-Z0-9_]/;
	// After some profiling, the cache seems to be unnecessary because tokenizePath
	// (the slowest part of adding a route) is very fast
	// const tokenCache = new Map<string, Token[][]>()
	function tokenizePath(path) {
		if (!path)
			return [
				[]
			];
		if (path === '/')
			return [
				[ROOT_TOKEN]
			];
		if (!path.startsWith('/')) {
			throw new Error(`Route paths should start with a "/": "${path}" should be "/${path}".`);
		}
		// if (tokenCache.has(path)) return tokenCache.get(path)!
		function crash(message) {
			throw new Error(`ERR (${state})/"${buffer}": ${message}`);
		}
		let state = 0 /* Static */ ;
		let previousState = state;
		const tokens = [];
		// the segment will always be valid because we get into the initial state
		// with the leading /
		let segment;

		function finalizeSegment() {
			if (segment)
				tokens.push(segment);
			segment = [];
		}
		// index on the path
		let i = 0;
		// char at index
		let char;
		// buffer of the value read
		let buffer = '';
		// custom regexp for a param
		let customRe = '';

		function consumeBuffer() {
			if (!buffer)
				return;
			if (state === 0 /* Static */ ) {
				segment.push({
					type: 0 /* Static */ ,
					value: buffer,
				});
			} else if (state === 1 /* Param */ ||
				state === 2 /* ParamRegExp */ ||
				state === 3 /* ParamRegExpEnd */ ) {
				if (segment.length > 1 && (char === '*' || char === '+'))
					crash(`A repeatable param (${buffer}) must be alone in its segment. eg: '/:ids+.`);
				segment.push({
					type: 1 /* Param */ ,
					value: buffer,
					regexp: customRe,
					repeatable: char === '*' || char === '+',
					optional: char === '*' || char === '?',
				});
			} else {
				crash('Invalid state to consume buffer');
			}
			buffer = '';
		}

		function addCharToBuffer() {
			buffer += char;
		}
		while (i < path.length) {
			char = path[i++];
			if (char === '\\' && state !== 2 /* ParamRegExp */ ) {
				previousState = state;
				state = 4 /* EscapeNext */ ;
				continue;
			}
			switch (state) {
				case 0 /* Static */ :
					if (char === '/') {
						if (buffer) {
							consumeBuffer();
						}
						finalizeSegment();
					} else if (char === ':') {
						consumeBuffer();
						state = 1 /* Param */ ;
					} else {
						addCharToBuffer();
					}
					break;
				case 4 /* EscapeNext */ :
					addCharToBuffer();
					state = previousState;
					break;
				case 1 /* Param */ :
					if (char === '(') {
						state = 2 /* ParamRegExp */ ;
					} else if (VALID_PARAM_RE.test(char)) {
						addCharToBuffer();
					} else {
						consumeBuffer();
						state = 0 /* Static */ ;
						// go back one character if we were not modifying
						if (char !== '*' && char !== '?' && char !== '+')
							i--;
					}
					break;
				case 2 /* ParamRegExp */ :
					// TODO: is it worth handling nested regexp? like :p(?:prefix_([^/]+)_suffix)
					// it already works by escaping the closing )
					// https://paths.esm.dev/?p=AAMeJbiAwQEcDKbAoAAkP60PG2R6QAvgNaA6AFACM2ABuQBB#
					// is this really something people need since you can also write
					// /prefix_:p()_suffix
					if (char === ')') {
						// handle the escaped )
						if (customRe[customRe.length - 1] == '\\')
							customRe = customRe.slice(0, -1) + char;
						else
							state = 3 /* ParamRegExpEnd */ ;
					} else {
						customRe += char;
					}
					break;
				case 3 /* ParamRegExpEnd */ :
					// same as finalizing a param
					consumeBuffer();
					state = 0 /* Static */ ;
					// go back one character if we were not modifying
					if (char !== '*' && char !== '?' && char !== '+')
						i--;
					customRe = '';
					break;
				default:
					crash('Unknown state');
					break;
			}
		}
		if (state === 2 /* ParamRegExp */ )
			crash(`Unfinished custom RegExp for param "${buffer}"`);
		consumeBuffer();
		finalizeSegment();
		// tokenCache.set(path, tokens)
		return tokens;
	}

	function createRouteRecordMatcher(record, parent, options) {
		const parser = tokensToParser(tokenizePath(record.path), options);
		// warn against params with the same name
		{
			const existingKeys = new Set();
			for (const key of parser.keys) {
				if (existingKeys.has(key.name))
					warn(
						`Found duplicated params with name "${key.name}" for path "${record.path}". Only the last one will be available on "$route.params".`
						);
				existingKeys.add(key.name);
			}
		}
		const matcher = assign(parser, {
			record,
			parent,
			// these needs to be populated by the parent
			children: [],
			alias: [],
		});
		if (parent) {
			// both are aliases or both are not aliases
			// we don't want to mix them because the order is used when
			// passing originalRecord in Matcher.addRoute
			if (!matcher.record.aliasOf === !parent.record.aliasOf)
				parent.children.push(matcher);
		}
		return matcher;
	}

	/**
	 * Creates a Router Matcher.
	 *
	 * @internal
	 * @param routes - array of initial routes
	 * @param globalOptions - global route options
	 */
	function createRouterMatcher(routes, globalOptions) {
		// normalized ordered array of matchers
		const matchers = [];
		const matcherMap = new Map();
		globalOptions = mergeOptions({
			strict: false,
			end: true,
			sensitive: false
		}, globalOptions);

		function getRecordMatcher(name) {
			return matcherMap.get(name);
		}

		function addRoute(record, parent, originalRecord) {
			// used later on to remove by name
			const isRootAdd = !originalRecord;
			const mainNormalizedRecord = normalizeRouteRecord(record);
			// we might be the child of an alias
			mainNormalizedRecord.aliasOf = originalRecord && originalRecord.record;
			const options = mergeOptions(globalOptions, record);
			// generate an array of records to correctly handle aliases
			const normalizedRecords = [
				mainNormalizedRecord,
			];
			if ('alias' in record) {
				const aliases = typeof record.alias === 'string' ? [record.alias] : record.alias;
				for (const alias of aliases) {
					normalizedRecords.push(assign({}, mainNormalizedRecord, {
						// this allows us to hold a copy of the `components` option
						// so that async components cache is hold on the original record
						components: originalRecord ?
							originalRecord.record.components :
							mainNormalizedRecord.components,
						path: alias,
						// we might be the child of an alias
						aliasOf: originalRecord ?
							originalRecord.record :
							mainNormalizedRecord,
						// the aliases are always of the same kind as the original since they
						// are defined on the same record
					}));
				}
			}
			let matcher;
			let originalMatcher;
			for (const normalizedRecord of normalizedRecords) {
				const {
					path
				} = normalizedRecord;
				// Build up the path for nested routes if the child isn't an absolute
				// route. Only add the / delimiter if the child path isn't empty and if the
				// parent path doesn't have a trailing slash
				if (parent && path[0] !== '/') {
					const parentPath = parent.record.path;
					const connectingSlash = parentPath[parentPath.length - 1] === '/' ? '' : '/';
					normalizedRecord.path =
						parent.record.path + (path && connectingSlash + path);
				}
				if (normalizedRecord.path === '*') {
					throw new Error(
						'Catch all routes ("*") must now be defined using a param with a custom regexp.\n' +
						'See more at https://next.router.vuejs.org/guide/migration/#removed-star-or-catch-all-routes.'
						);
				}
				// create the object before hand so it can be passed to children
				matcher = createRouteRecordMatcher(normalizedRecord, parent, options);
				if (parent && path[0] === '/')
					checkMissingParamsInAbsolutePath(matcher, parent);
				// if we are an alias we must tell the original record that we exist
				// so we can be removed
				if (originalRecord) {
					originalRecord.alias.push(matcher); {
						checkSameParams(originalRecord, matcher);
					}
				} else {
					// otherwise, the first record is the original and others are aliases
					originalMatcher = originalMatcher || matcher;
					if (originalMatcher !== matcher)
						originalMatcher.alias.push(matcher);
					// remove the route if named and only for the top record (avoid in nested calls)
					// this works because the original record is the first one
					if (isRootAdd && record.name && !isAliasRecord(matcher))
						removeRoute(record.name);
				}
				if ('children' in mainNormalizedRecord) {
					const children = mainNormalizedRecord.children;
					for (let i = 0; i < children.length; i++) {
						addRoute(children[i], matcher, originalRecord && originalRecord.children[i]);
					}
				}
				// if there was no original record, then the first one was not an alias and all
				// other alias (if any) need to reference this record when adding children
				originalRecord = originalRecord || matcher;
				// TODO: add normalized records for more flexibility
				// if (parent && isAliasRecord(originalRecord)) {
				//   parent.children.push(originalRecord)
				// }
				insertMatcher(matcher);
			}
			return originalMatcher ?
				() => {
					// since other matchers are aliases, they should be removed by the original matcher
					removeRoute(originalMatcher);
				} :
				noop;
		}

		function removeRoute(matcherRef) {
			if (isRouteName(matcherRef)) {
				const matcher = matcherMap.get(matcherRef);
				if (matcher) {
					matcherMap.delete(matcherRef);
					matchers.splice(matchers.indexOf(matcher), 1);
					matcher.children.forEach(removeRoute);
					matcher.alias.forEach(removeRoute);
				}
			} else {
				const index = matchers.indexOf(matcherRef);
				if (index > -1) {
					matchers.splice(index, 1);
					if (matcherRef.record.name)
						matcherMap.delete(matcherRef.record.name);
					matcherRef.children.forEach(removeRoute);
					matcherRef.alias.forEach(removeRoute);
				}
			}
		}

		function getRoutes() {
			return matchers;
		}

		function insertMatcher(matcher) {
			let i = 0;
			while (i < matchers.length &&
				comparePathParserScore(matcher, matchers[i]) >= 0 &&
				// Adding children with empty path should still appear before the parent
				// https://github.com/vuejs/router/issues/1124
				(matcher.record.path !== matchers[i].record.path ||
					!isRecordChildOf(matcher, matchers[i])))
				i++;
			matchers.splice(i, 0, matcher);
			// only add the original record to the name map
			if (matcher.record.name && !isAliasRecord(matcher))
				matcherMap.set(matcher.record.name, matcher);
		}

		function resolve(location, currentLocation) {
			let matcher;
			let params = {};
			let path;
			let name;
			if ('name' in location && location.name) {
				matcher = matcherMap.get(location.name);
				if (!matcher)
					throw createRouterError(1 /* MATCHER_NOT_FOUND */ , {
						location,
					});
				name = matcher.record.name;
				params = assign(
					// paramsFromLocation is a new object
					paramsFromLocation(currentLocation.params,
						// only keep params that exist in the resolved location
						// TODO: only keep optional params coming from a parent record
						matcher.keys.filter(k => !k.optional).map(k => k.name)), location.params);
				// throws if cannot be stringified
				path = matcher.stringify(params);
			} else if ('path' in location) {
				// no need to resolve the path with the matcher as it was provided
				// this also allows the user to control the encoding
				path = location.path;
				if (!path.startsWith('/')) {
					warn(
						`The Matcher cannot resolve relative paths but received "${path}". Unless you directly called \`matcher.resolve("${path}")\`, this is probably a bug in vue-router. Please open an issue at https://new-issue.vuejs.org/?repo=vuejs/router.`);
				}
				matcher = matchers.find(m => m.re.test(path));
				// matcher should have a value after the loop
				if (matcher) {
					// TODO: dev warning of unused params if provided
					// we know the matcher works because we tested the regexp
					params = matcher.parse(path);
					name = matcher.record.name;
				}
				// location is a relative path
			} else {
				// match by name or path of current route
				matcher = currentLocation.name ?
					matcherMap.get(currentLocation.name) :
					matchers.find(m => m.re.test(currentLocation.path));
				if (!matcher)
					throw createRouterError(1 /* MATCHER_NOT_FOUND */ , {
						location,
						currentLocation,
					});
				name = matcher.record.name;
				// since we are navigating to the same location, we don't need to pick the
				// params like when `name` is provided
				params = assign({}, currentLocation.params, location.params);
				path = matcher.stringify(params);
			}
			const matched = [];
			let parentMatcher = matcher;
			while (parentMatcher) {
				// reversed order so parents are at the beginning
				matched.unshift(parentMatcher.record);
				parentMatcher = parentMatcher.parent;
			}
			return {
				name,
				path,
				params,
				matched,
				meta: mergeMetaFields(matched),
			};
		}
		// add initial routes
		routes.forEach(route => addRoute(route));
		return {
			addRoute,
			resolve,
			removeRoute,
			getRoutes,
			getRecordMatcher
		};
	}

	function paramsFromLocation(params, keys) {
		const newParams = {};
		for (const key of keys) {
			if (key in params)
				newParams[key] = params[key];
		}
		return newParams;
	}
	/**
	 * Normalizes a RouteRecordRaw. Creates a copy
	 *
	 * @param record
	 * @returns the normalized version
	 */
	function normalizeRouteRecord(record) {
		return {
			path: record.path,
			redirect: record.redirect,
			name: record.name,
			meta: record.meta || {},
			aliasOf: undefined,
			beforeEnter: record.beforeEnter,
			props: normalizeRecordProps(record),
			children: record.children || [],
			instances: {},
			leaveGuards: new Set(),
			updateGuards: new Set(),
			enterCallbacks: {},
			components: 'components' in record ?
				record.components || {} :
				{
					default: record.component
				},
		};
	}
	/**
	 * Normalize the optional `props` in a record to always be an object similar to
	 * components. Also accept a boolean for components.
	 * @param record
	 */
	function normalizeRecordProps(record) {
		const propsObject = {};
		// props does not exist on redirect records but we can set false directly
		const props = record.props || false;
		if ('component' in record) {
			propsObject.default = props;
		} else {
			// NOTE: we could also allow a function to be applied to every component.
			// Would need user feedback for use cases
			for (const name in record.components)
				propsObject[name] = typeof props === 'boolean' ? props : props[name];
		}
		return propsObject;
	}
	/**
	 * Checks if a record or any of its parent is an alias
	 * @param record
	 */
	function isAliasRecord(record) {
		while (record) {
			if (record.record.aliasOf)
				return true;
			record = record.parent;
		}
		return false;
	}
	/**
	 * Merge meta fields of an array of records
	 *
	 * @param matched - array of matched records
	 */
	function mergeMetaFields(matched) {
		return matched.reduce((meta, record) => assign(meta, record.meta), {});
	}

	function mergeOptions(defaults, partialOptions) {
		const options = {};
		for (const key in defaults) {
			options[key] = key in partialOptions ? partialOptions[key] : defaults[key];
		}
		return options;
	}

	function isSameParam(a, b) {
		return (a.name === b.name &&
			a.optional === b.optional &&
			a.repeatable === b.repeatable);
	}
	/**
	 * Check if a path and its alias have the same required params
	 *
	 * @param a - original record
	 * @param b - alias record
	 */
	function checkSameParams(a, b) {
		for (const key of a.keys) {
			if (!key.optional && !b.keys.find(isSameParam.bind(null, key)))
				return warn(
					`Alias "${b.record.path}" and the original record: "${a.record.path}" should have the exact same param named "${key.name}"`
					);
		}
		for (const key of b.keys) {
			if (!key.optional && !a.keys.find(isSameParam.bind(null, key)))
				return warn(
					`Alias "${b.record.path}" and the original record: "${a.record.path}" should have the exact same param named "${key.name}"`
					);
		}
	}

	function checkMissingParamsInAbsolutePath(record, parent) {
		for (const key of parent.keys) {
			if (!record.keys.find(isSameParam.bind(null, key)))
				return warn(
					`Absolute path "${record.record.path}" should have the exact same param named "${key.name}" as its parent "${parent.record.path}".`
					);
		}
	}

	function isRecordChildOf(record, parent) {
		return parent.children.some(child => child === record || isRecordChildOf(record, child));
	}

	/**
	 * Encoding Rules ␣ = Space Path: ␣ " < > # ? { } Query: ␣ " < > # & = Hash: ␣ "
	 * < > `
	 *
	 * On top of that, the RFC3986 (https://tools.ietf.org/html/rfc3986#section-2.2)
	 * defines some extra characters to be encoded. Most browsers do not encode them
	 * in encodeURI https://github.com/whatwg/url/issues/369, so it may be safer to
	 * also encode `!'()*`. Leaving unencoded only ASCII alphanumeric(`a-zA-Z0-9`)
	 * plus `-._~`. This extra safety should be applied to query by patching the
	 * string returned by encodeURIComponent encodeURI also encodes `[\]^`. `\`
	 * should be encoded to avoid ambiguity. Browsers (IE, FF, C) transform a `\`
	 * into a `/` if directly typed in. The _backtick_ (`````) should also be
	 * encoded everywhere because some browsers like FF encode it when directly
	 * written while others don't. Safari and IE don't encode ``"<>{}``` in hash.
	 */
	// const EXTRA_RESERVED_RE = /[!'()*]/g
	// const encodeReservedReplacer = (c: string) => '%' + c.charCodeAt(0).toString(16)
	const HASH_RE = /#/g; // %23
	const AMPERSAND_RE = /&/g; // %26
	const SLASH_RE = /\//g; // %2F
	const EQUAL_RE = /=/g; // %3D
	const IM_RE = /\?/g; // %3F
	const PLUS_RE = /\+/g; // %2B
	/**
	 * NOTE: It's not clear to me if we should encode the + symbol in queries, it
	 * seems to be less flexible than not doing so and I can't find out the legacy
	 * systems requiring this for regular requests like text/html. In the standard,
	 * the encoding of the plus character is only mentioned for
	 * application/x-www-form-urlencoded
	 * (https://url.spec.whatwg.org/#urlencoded-parsing) and most browsers seems lo
	 * leave the plus character as is in queries. To be more flexible, we allow the
	 * plus character on the query but it can also be manually encoded by the user.
	 *
	 * Resources:
	 * - https://url.spec.whatwg.org/#urlencoded-parsing
	 * - https://stackoverflow.com/questions/1634271/url-encoding-the-space-character-or-20
	 */
	const ENC_BRACKET_OPEN_RE = /%5B/g; // [
	const ENC_BRACKET_CLOSE_RE = /%5D/g; // ]
	const ENC_CARET_RE = /%5E/g; // ^
	const ENC_BACKTICK_RE = /%60/g; // `
	const ENC_CURLY_OPEN_RE = /%7B/g; // {
	const ENC_PIPE_RE = /%7C/g; // |
	const ENC_CURLY_CLOSE_RE = /%7D/g; // }
	const ENC_SPACE_RE = /%20/g; // }
	/**
	 * Encode characters that need to be encoded on the path, search and hash
	 * sections of the URL.
	 *
	 * @internal
	 * @param text - string to encode
	 * @returns encoded string
	 */
	function commonEncode(text) {
		return encodeURI('' + text)
			.replace(ENC_PIPE_RE, '|')
			.replace(ENC_BRACKET_OPEN_RE, '[')
			.replace(ENC_BRACKET_CLOSE_RE, ']');
	}
	/**
	 * Encode characters that need to be encoded on the hash section of the URL.
	 *
	 * @param text - string to encode
	 * @returns encoded string
	 */
	function encodeHash(text) {
		return commonEncode(text)
			.replace(ENC_CURLY_OPEN_RE, '{')
			.replace(ENC_CURLY_CLOSE_RE, '}')
			.replace(ENC_CARET_RE, '^');
	}
	/**
	 * Encode characters that need to be encoded query values on the query
	 * section of the URL.
	 *
	 * @param text - string to encode
	 * @returns encoded string
	 */
	function encodeQueryValue(text) {
		return (commonEncode(text)
			// Encode the space as +, encode the + to differentiate it from the space
			.replace(PLUS_RE, '%2B')
			.replace(ENC_SPACE_RE, '+')
			.replace(HASH_RE, '%23')
			.replace(AMPERSAND_RE, '%26')
			.replace(ENC_BACKTICK_RE, '`')
			.replace(ENC_CURLY_OPEN_RE, '{')
			.replace(ENC_CURLY_CLOSE_RE, '}')
			.replace(ENC_CARET_RE, '^'));
	}
	/**
	 * Like `encodeQueryValue` but also encodes the `=` character.
	 *
	 * @param text - string to encode
	 */
	function encodeQueryKey(text) {
		return encodeQueryValue(text).replace(EQUAL_RE, '%3D');
	}
	/**
	 * Encode characters that need to be encoded on the path section of the URL.
	 *
	 * @param text - string to encode
	 * @returns encoded string
	 */
	function encodePath(text) {
		return commonEncode(text).replace(HASH_RE, '%23').replace(IM_RE, '%3F');
	}
	/**
	 * Encode characters that need to be encoded on the path section of the URL as a
	 * param. This function encodes everything {@link encodePath} does plus the
	 * slash (`/`) character. If `text` is `null` or `undefined`, returns an empty
	 * string instead.
	 *
	 * @param text - string to encode
	 * @returns encoded string
	 */
	function encodeParam(text) {
		return text == null ? '' : encodePath(text).replace(SLASH_RE, '%2F');
	}
	/**
	 * Decode text using `decodeURIComponent`. Returns the original text if it
	 * fails.
	 *
	 * @param text - string to decode
	 * @returns decoded string
	 */
	function decode(text) {
		try {
			return decodeURIComponent('' + text);
		} catch (err) {
			warn(`Error decoding "${text}". Using original value`);
		}
		return '' + text;
	}

	/**
	 * Transforms a queryString into a {@link LocationQuery} object. Accept both, a
	 * version with the leading `?` and without Should work as URLSearchParams

	 * @internal
	 *
	 * @param search - search string to parse
	 * @returns a query object
	 */
	function parseQuery(search) {
		const query = {};
		// avoid creating an object with an empty key and empty value
		// because of split('&')
		if (search === '' || search === '?')
			return query;
		const hasLeadingIM = search[0] === '?';
		const searchParams = (hasLeadingIM ? search.slice(1) : search).split('&');
		for (let i = 0; i < searchParams.length; ++i) {
			// pre decode the + into space
			const searchParam = searchParams[i].replace(PLUS_RE, ' ');
			// allow the = character
			const eqPos = searchParam.indexOf('=');
			const key = decode(eqPos < 0 ? searchParam : searchParam.slice(0, eqPos));
			const value = eqPos < 0 ? null : decode(searchParam.slice(eqPos + 1));
			if (key in query) {
				// an extra variable for ts types
				let currentValue = query[key];
				if (!Array.isArray(currentValue)) {
					currentValue = query[key] = [currentValue];
				}
				currentValue.push(value);
			} else {
				query[key] = value;
			}
		}
		return query;
	}
	/**
	 * Stringifies a {@link LocationQueryRaw} object. Like `URLSearchParams`, it
	 * doesn't prepend a `?`
	 *
	 * @internal
	 *
	 * @param query - query object to stringify
	 * @returns string version of the query without the leading `?`
	 */
	function stringifyQuery(query) {
		let search = '';
		for (let key in query) {
			const value = query[key];
			key = encodeQueryKey(key);
			if (value == null) {
				// only null adds the value
				if (value !== undefined) {
					search += (search.length ? '&' : '') + key;
				}
				continue;
			}
			// keep null values
			const values = Array.isArray(value) ?
				value.map(v => v && encodeQueryValue(v)) :
				[value && encodeQueryValue(value)];
			values.forEach(value => {
				// skip undefined values in arrays as if they were not present
				// smaller code than using filter
				if (value !== undefined) {
					// only append & with non-empty search
					search += (search.length ? '&' : '') + key;
					if (value != null)
						search += '=' + value;
				}
			});
		}
		return search;
	}
	/**
	 * Transforms a {@link LocationQueryRaw} into a {@link LocationQuery} by casting
	 * numbers into strings, removing keys with an undefined value and replacing
	 * undefined with null in arrays
	 *
	 * @param query - query object to normalize
	 * @returns a normalized query object
	 */
	function normalizeQuery(query) {
		const normalizedQuery = {};
		for (const key in query) {
			const value = query[key];
			if (value !== undefined) {
				normalizedQuery[key] = Array.isArray(value) ?
					value.map(v => (v == null ? null : '' + v)) :
					value == null ?
					value :
					'' + value;
			}
		}
		return normalizedQuery;
	}

	/**
	 * Create a list of callbacks that can be reset. Used to create before and after navigation guards list
	 */
	function useCallbacks() {
		let handlers = [];

		function add(handler) {
			handlers.push(handler);
			return () => {
				const i = handlers.indexOf(handler);
				if (i > -1)
					handlers.splice(i, 1);
			};
		}

		function reset() {
			handlers = [];
		}
		return {
			add,
			list: () => handlers,
			reset,
		};
	}

	function registerGuard(record, name, guard) {
		const removeFromList = () => {
			record[name].delete(guard);
		};
		vue.onUnmounted(removeFromList);
		vue.onDeactivated(removeFromList);
		vue.onActivated(() => {
			record[name].add(guard);
		});
		record[name].add(guard);
	}
	/**
	 * Add a navigation guard that triggers whenever the component for the current
	 * location is about to be left. Similar to {@link beforeRouteLeave} but can be
	 * used in any component. The guard is removed when the component is unmounted.
	 *
	 * @param leaveGuard - {@link NavigationGuard}
	 */
	function onBeforeRouteLeave(leaveGuard) {
		if (!vue.getCurrentInstance()) {
			warn(
				'getCurrentInstance() returned null. onBeforeRouteLeave() must be called at the top of a setup function');
			return;
		}
		const activeRecord = vue.inject(matchedRouteKey,
			// to avoid warning
			{}).value;
		if (!activeRecord) {
			warn(
				'No active route record was found when calling `onBeforeRouteLeave()`. Make sure you call this function inside of a component child of <router-view>. Maybe you called it inside of App.vue?');
			return;
		}
		registerGuard(activeRecord, 'leaveGuards', leaveGuard);
	}
	/**
	 * Add a navigation guard that triggers whenever the current location is about
	 * to be updated. Similar to {@link beforeRouteUpdate} but can be used in any
	 * component. The guard is removed when the component is unmounted.
	 *
	 * @param updateGuard - {@link NavigationGuard}
	 */
	function onBeforeRouteUpdate(updateGuard) {
		if (!vue.getCurrentInstance()) {
			warn(
				'getCurrentInstance() returned null. onBeforeRouteUpdate() must be called at the top of a setup function');
			return;
		}
		const activeRecord = vue.inject(matchedRouteKey,
			// to avoid warning
			{}).value;
		if (!activeRecord) {
			warn(
				'No active route record was found when calling `onBeforeRouteUpdate()`. Make sure you call this function inside of a component child of <router-view>. Maybe you called it inside of App.vue?');
			return;
		}
		registerGuard(activeRecord, 'updateGuards', updateGuard);
	}

	function guardToPromiseFn(guard, to, from, record, name) {
		// keep a reference to the enterCallbackArray to prevent pushing callbacks if a new navigation took place
		const enterCallbackArray = record &&
			// name is defined if record is because of the function overload
			(record.enterCallbacks[name] = record.enterCallbacks[name] || []);
		return () => new Promise((resolve, reject) => {
			const next = (valid) => {
				if (valid === false)
					reject(createRouterError(4 /* NAVIGATION_ABORTED */ , {
						from,
						to,
					}));
				else if (valid instanceof Error) {
					reject(valid);
				} else if (isRouteLocation(valid)) {
					reject(createRouterError(2 /* NAVIGATION_GUARD_REDIRECT */ , {
						from: to,
						to: valid,
					}));
				} else {
					if (enterCallbackArray &&
						// since enterCallbackArray is truthy, both record and name also are
						record.enterCallbacks[name] === enterCallbackArray &&
						typeof valid === 'function')
						enterCallbackArray.push(valid);
					resolve();
				}
			};
			// wrapping with Promise.resolve allows it to work with both async and sync guards
			const guardReturn = guard.call(record && record.instances[name], to, from,
				canOnlyBeCalledOnce(next, to, from));
			let guardCall = Promise.resolve(guardReturn);
			if (guard.length < 3)
				guardCall = guardCall.then(next);
			if (guard.length > 2) {
				const message =
					`The "next" callback was never called inside of ${guard.name ? '"' + guard.name + '"' : ''}:\n${guard.toString()}\n. If you are returning a value instead of calling "next", make sure to remove the "next" parameter from your function.`;
				if (typeof guardReturn === 'object' && 'then' in guardReturn) {
					guardCall = guardCall.then(resolvedValue => {
						// @ts-expect-error: _called is added at canOnlyBeCalledOnce
						if (!next._called) {
							warn(message);
							return Promise.reject(new Error('Invalid navigation guard'));
						}
						return resolvedValue;
					});
					// TODO: test me!
				} else if (guardReturn !== undefined) {
					// @ts-expect-error: _called is added at canOnlyBeCalledOnce
					if (!next._called) {
						warn(message);
						reject(new Error('Invalid navigation guard'));
						return;
					}
				}
			}
			guardCall.catch(err => reject(err));
		});
	}

	function canOnlyBeCalledOnce(next, to, from) {
		let called = 0;
		return function() {
			if (called++ === 1)
				warn(
					`The "next" callback was called more than once in one navigation guard when going from "${from.fullPath}" to "${to.fullPath}". It should be called exactly one time in each navigation guard. This will fail in production.`
					);
			// @ts-expect-error: we put it in the original one because it's easier to check
			next._called = true;
			if (called === 1)
				next.apply(null, arguments);
		};
	}

	function extractComponentsGuards(matched, guardType, to, from) {
		const guards = [];
		for (const record of matched) {
			for (const name in record.components) {
				let rawComponent = record.components[name]; {
					if (!rawComponent ||
						(typeof rawComponent !== 'object' &&
							typeof rawComponent !== 'function')) {
						warn(`Component "${name}" in record with path "${record.path}" is not` +
							` a valid component. Received "${String(rawComponent)}".`);
						// throw to ensure we stop here but warn to ensure the message isn't
						// missed by the user
						throw new Error('Invalid route component');
					} else if ('then' in rawComponent) {
						// warn if user wrote import('/component.vue') instead of () =>
						// import('./component.vue')
						warn(`Component "${name}" in record with path "${record.path}" is a ` +
							`Promise instead of a function that returns a Promise. Did you ` +
							`write "import('./MyPage.vue')" instead of ` +
							`"() => import('./MyPage.vue')" ? This will break in ` +
							`production if not fixed.`);
						const promise = rawComponent;
						rawComponent = () => promise;
					} else if (rawComponent.__asyncLoader &&
						// warn only once per component
						!rawComponent.__warnedDefineAsync) {
						rawComponent.__warnedDefineAsync = true;
						warn(`Component "${name}" in record with path "${record.path}" is defined ` +
							`using "defineAsyncComponent()". ` +
							`Write "() => import('./MyPage.vue')" instead of ` +
							`"defineAsyncComponent(() => import('./MyPage.vue'))".`);
					}
				}
				// skip update and leave guards if the route component is not mounted
				if (guardType !== 'beforeRouteEnter' && !record.instances[name])
					continue;
				if (isRouteComponent(rawComponent)) {
					// __vccOpts is added by vue-class-component and contain the regular options
					const options = rawComponent.__vccOpts || rawComponent;
					const guard = options[guardType];
					guard && guards.push(guardToPromiseFn(guard, to, from, record, name));
				} else {
					// start requesting the chunk already
					let componentPromise = rawComponent();
					if (!('catch' in componentPromise)) {
						warn(
							`Component "${name}" in record with path "${record.path}" is a function that does not return a Promise. If you were passing a functional component, make sure to add a "displayName" to the component. This will break in production if not fixed.`);
						componentPromise = Promise.resolve(componentPromise);
					}
					guards.push(() => componentPromise.then(resolved => {
						if (!resolved)
							return Promise.reject(new Error(
								`Couldn't resolve component "${name}" at "${record.path}"`));
						const resolvedComponent = isESModule(resolved) ?
							resolved.default :
							resolved;
						// replace the function with the resolved component
						record.components[name] = resolvedComponent;
						// __vccOpts is added by vue-class-component and contain the regular options
						const options = resolvedComponent.__vccOpts || resolvedComponent;
						const guard = options[guardType];
						return guard && guardToPromiseFn(guard, to, from, record, name)();
					}));
				}
			}
		}
		return guards;
	}
	/**
	 * Allows differentiating lazy components from functional components and vue-class-component
	 *
	 * @param component
	 */
	function isRouteComponent(component) {
		return (typeof component === 'object' ||
			'displayName' in component ||
			'props' in component ||
			'__vccOpts' in component);
	}

	// TODO: we could allow currentRoute as a prop to expose `isActive` and
	// `isExactActive` behavior should go through an RFC
	function useLink(props) {
		const router = vue.inject(routerKey);
		const currentRoute = vue.inject(routeLocationKey);
		const route = vue.computed(() => router.resolve(vue.unref(props.to)));
		const activeRecordIndex = vue.computed(() => {
			const {
				matched
			} = route.value;
			const {
				length
			} = matched;
			const routeMatched = matched[length - 1];
			const currentMatched = currentRoute.matched;
			if (!routeMatched || !currentMatched.length)
				return -1;
			const index = currentMatched.findIndex(isSameRouteRecord.bind(null, routeMatched));
			if (index > -1)
				return index;
			// possible parent record
			const parentRecordPath = getOriginalPath(matched[length - 2]);
			return (
				// we are dealing with nested routes
				length > 1 &&
				// if the parent and matched route have the same path, this link is
				// referring to the empty child. Or we currently are on a different
				// child of the same parent
				getOriginalPath(routeMatched) === parentRecordPath &&
				// avoid comparing the child with its parent
				currentMatched[currentMatched.length - 1].path !== parentRecordPath ?
				currentMatched.findIndex(isSameRouteRecord.bind(null, matched[length - 2])) :
				index);
		});
		const isActive = vue.computed(() => activeRecordIndex.value > -1 &&
			includesParams(currentRoute.params, route.value.params));
		const isExactActive = vue.computed(() => activeRecordIndex.value > -1 &&
			activeRecordIndex.value === currentRoute.matched.length - 1 &&
			isSameRouteLocationParams(currentRoute.params, route.value.params));

		function navigate(e = {}) {
			if (guardEvent(e)) {
				return router[vue.unref(props.replace) ? 'replace' : 'push'](vue.unref(props.to)
					// avoid uncaught errors are they are logged anyway
				).catch(noop);
			}
			return Promise.resolve();
		}
		// devtools only
		if (isBrowser) {
			const instance = vue.getCurrentInstance();
			if (instance) {
				const linkContextDevtools = {
					route: route.value,
					isActive: isActive.value,
					isExactActive: isExactActive.value,
				};
				// @ts-expect-error: this is internal
				instance.__vrl_devtools = instance.__vrl_devtools || [];
				// @ts-expect-error: this is internal
				instance.__vrl_devtools.push(linkContextDevtools);
				vue.watchEffect(() => {
					linkContextDevtools.route = route.value;
					linkContextDevtools.isActive = isActive.value;
					linkContextDevtools.isExactActive = isExactActive.value;
				}, {
					flush: 'post'
				});
			}
		}
		return {
			route,
			href: vue.computed(() => route.value.href),
			isActive,
			isExactActive,
			navigate,
		};
	}
	const RouterLinkImpl = /*#__PURE__*/ vue.defineComponent({
		name: 'RouterLink',
		props: {
			to: {
				type: [String, Object],
				required: true,
			},
			replace: Boolean,
			activeClass: String,
			// inactiveClass: String,
			exactActiveClass: String,
			custom: Boolean,
			ariaCurrentValue: {
				type: String,
				default: 'page',
			},
		},
		useLink,
		setup(props, {
			slots
		}) {
			const link = vue.reactive(useLink(props));
			const {
				options
			} = vue.inject(routerKey);
			const elClass = vue.computed(() => ({
				[getLinkClass(props.activeClass, options.linkActiveClass,
					'router-link-active')]: link.isActive,
				// [getLinkClass(
				//   props.inactiveClass,
				//   options.linkInactiveClass,
				//   'router-link-inactive'
				// )]: !link.isExactActive,
				[getLinkClass(props.exactActiveClass, options.linkExactActiveClass,
					'router-link-exact-active')]: link.isExactActive,
			}));
			return () => {
				const children = slots.default && slots.default(link);
				return props.custom ?
					children :
					vue.h('a', {
						'aria-current': link.isExactActive ?
							props.ariaCurrentValue :
							null,
						href: link.href,
						// this would override user added attrs but Vue will still add
						// the listener so we end up triggering both
						onClick: link.navigate,
						class: elClass.value,
					}, children);
			};
		},
	});
	// export the public type for h/tsx inference
	// also to avoid inline import() in generated d.ts files
	/**
	 * Component to render a link that triggers a navigation on click.
	 */
	const RouterLink = RouterLinkImpl;

	function guardEvent(e) {
		// don't redirect with control keys
		if (e.metaKey || e.altKey || e.ctrlKey || e.shiftKey)
			return;
		// don't redirect when preventDefault called
		if (e.defaultPrevented)
			return;
		// don't redirect on right click
		if (e.button !== undefined && e.button !== 0)
			return;
		// don't redirect if `target="_blank"`
		// @ts-expect-error getAttribute does exist
		if (e.currentTarget && e.currentTarget.getAttribute) {
			// @ts-expect-error getAttribute exists
			const target = e.currentTarget.getAttribute('target');
			if (/\b_blank\b/i.test(target))
				return;
		}
		// this may be a Weex event which doesn't have this method
		if (e.preventDefault)
			e.preventDefault();
		return true;
	}

	function includesParams(outer, inner) {
		for (const key in inner) {
			const innerValue = inner[key];
			const outerValue = outer[key];
			if (typeof innerValue === 'string') {
				if (innerValue !== outerValue)
					return false;
			} else {
				if (!Array.isArray(outerValue) ||
					outerValue.length !== innerValue.length ||
					innerValue.some((value, i) => value !== outerValue[i]))
					return false;
			}
		}
		return true;
	}
	/**
	 * Get the original path value of a record by following its aliasOf
	 * @param record
	 */
	function getOriginalPath(record) {
		return record ? (record.aliasOf ? record.aliasOf.path : record.path) : '';
	}
	/**
	 * Utility class to get the active class based on defaults.
	 * @param propClass
	 * @param globalClass
	 * @param defaultClass
	 */
	const getLinkClass = (propClass, globalClass, defaultClass) => propClass != null ?
		propClass :
		globalClass != null ?
		globalClass :
		defaultClass;

	const RouterViewImpl = /*#__PURE__*/ vue.defineComponent({
		name: 'RouterView',
		// #674 we manually inherit them
		inheritAttrs: false,
		props: {
			name: {
				type: String,
				default: 'default',
			},
			route: Object,
		},
		// Better compat for @vue/compat users
		// https://github.com/vuejs/router/issues/1315
		compatConfig: {
			MODE: 3
		},
		setup(props, {
			attrs,
			slots
		}) {
			warnDeprecatedUsage();
			const injectedRoute = vue.inject(routerViewLocationKey);
			const routeToDisplay = vue.computed(() => props.route || injectedRoute.value);
			const depth = vue.inject(viewDepthKey, 0);
			const matchedRouteRef = vue.computed(() => routeToDisplay.value.matched[depth]);
			vue.provide(viewDepthKey, depth + 1);
			vue.provide(matchedRouteKey, matchedRouteRef);
			vue.provide(routerViewLocationKey, routeToDisplay);
			const viewRef = vue.ref();
			// watch at the same time the component instance, the route record we are
			// rendering, and the name
			vue.watch(() => [viewRef.value, matchedRouteRef.value, props.name], ([instance, to, name], [
				oldInstance, from, oldName
			]) => {
				// copy reused instances
				if (to) {
					// this will update the instance for new instances as well as reused
					// instances when navigating to a new route
					to.instances[name] = instance;
					// the component instance is reused for a different route or name so
					// we copy any saved update or leave guards. With async setup, the
					// mounting component will mount before the matchedRoute changes,
					// making instance === oldInstance, so we check if guards have been
					// added before. This works because we remove guards when
					// unmounting/deactivating components
					if (from && from !== to && instance && instance === oldInstance) {
						if (!to.leaveGuards.size) {
							to.leaveGuards = from.leaveGuards;
						}
						if (!to.updateGuards.size) {
							to.updateGuards = from.updateGuards;
						}
					}
				}
				// trigger beforeRouteEnter next callbacks
				if (instance &&
					to &&
					// if there is no instance but to and from are the same this might be
					// the first visit
					(!from || !isSameRouteRecord(to, from) || !oldInstance)) {
					(to.enterCallbacks[name] || []).forEach(callback => callback(instance));
				}
			}, {
				flush: 'post'
			});
			return () => {
				const route = routeToDisplay.value;
				const matchedRoute = matchedRouteRef.value;
				const ViewComponent = matchedRoute && matchedRoute.components[props.name];
				// we need the value at the time we render because when we unmount, we
				// navigated to a different location so the value is different
				const currentName = props.name;
				if (!ViewComponent) {
					return normalizeSlot(slots.default, {
						Component: ViewComponent,
						route
					});
				}
				// props from route configuration
				const routePropsOption = matchedRoute.props[props.name];
				const routeProps = routePropsOption ?
					routePropsOption === true ?
					route.params :
					typeof routePropsOption === 'function' ?
					routePropsOption(route) :
					routePropsOption :
					null;
				const onVnodeUnmounted = vnode => {
					// remove the instance reference to prevent leak
					if (vnode.component.isUnmounted) {
						matchedRoute.instances[currentName] = null;
					}
				};
				const component = vue.h(ViewComponent, assign({}, routeProps, attrs, {
					onVnodeUnmounted,
					ref: viewRef,
				}));
				if (isBrowser &&
					component.ref) {
					// TODO: can display if it's an alias, its props
					const info = {
						depth,
						name: matchedRoute.name,
						path: matchedRoute.path,
						meta: matchedRoute.meta,
					};
					const internalInstances = Array.isArray(component.ref) ?
						component.ref.map(r => r.i) :
						[component.ref.i];
					internalInstances.forEach(instance => {
						// @ts-expect-error
						instance.__vrv_devtools = info;
					});
				}
				return (
					// pass the vnode to the slot as a prop.
					// h and <component :is="..."> both accept vnodes
					normalizeSlot(slots.default, {
						Component: component,
						route
					}) ||
					component);
			};
		},
	});

	function normalizeSlot(slot, data) {
		if (!slot)
			return null;
		const slotContent = slot(data);
		return slotContent.length === 1 ? slotContent[0] : slotContent;
	}
	// export the public type for h/tsx inference
	// also to avoid inline import() in generated d.ts files
	/**
	 * Component to display the current route the user is at.
	 */
	const RouterView = RouterViewImpl;
	// warn against deprecated usage with <transition> & <keep-alive>
	// due to functional component being no longer eager in Vue 3
	function warnDeprecatedUsage() {
		const instance = vue.getCurrentInstance();
		const parentName = instance.parent && instance.parent.type.name;
		if (parentName &&
			(parentName === 'KeepAlive' || parentName.includes('Transition'))) {
			const comp = parentName === 'KeepAlive' ? 'keep-alive' : 'transition';
			warn(`<router-view> can no longer be used directly inside <transition> or <keep-alive>.\n` +
				`Use slot props instead:\n\n` +
				`<router-view v-slot="{ Component }">\n` +
				`  <${comp}>\n` +
				`    <component :is="Component" />\n` +
				`  </${comp}>\n` +
				`</router-view>`);
		}
	}

	function getDevtoolsGlobalHook() {
		return getTarget().__VUE_DEVTOOLS_GLOBAL_HOOK__;
	}

	function getTarget() {
		// @ts-ignore
		return (typeof navigator !== 'undefined' && typeof window !== 'undefined') ?
			window :
			typeof global !== 'undefined' ?
			global :
			{};
	}
	const isProxyAvailable = typeof Proxy === 'function';

	const HOOK_SETUP = 'devtools-plugin:setup';
	const HOOK_PLUGIN_SETTINGS_SET = 'plugin:settings:set';

	class ApiProxy {
		constructor(plugin, hook) {
			this.target = null;
			this.targetQueue = [];
			this.onQueue = [];
			this.plugin = plugin;
			this.hook = hook;
			const defaultSettings = {};
			if (plugin.settings) {
				for (const id in plugin.settings) {
					const item = plugin.settings[id];
					defaultSettings[id] = item.defaultValue;
				}
			}
			const localSettingsSaveId = `__vue-devtools-plugin-settings__${plugin.id}`;
			let currentSettings = Object.assign({}, defaultSettings);
			try {
				const raw = localStorage.getItem(localSettingsSaveId);
				const data = JSON.parse(raw);
				Object.assign(currentSettings, data);
			} catch (e) {
				// noop
			}
			this.fallbacks = {
				getSettings() {
					return currentSettings;
				},
				setSettings(value) {
					try {
						localStorage.setItem(localSettingsSaveId, JSON.stringify(value));
					} catch (e) {
						// noop
					}
					currentSettings = value;
				},
			};
			if (hook) {
				hook.on(HOOK_PLUGIN_SETTINGS_SET, (pluginId, value) => {
					if (pluginId === this.plugin.id) {
						this.fallbacks.setSettings(value);
					}
				});
			}
			this.proxiedOn = new Proxy({}, {
				get: (_target, prop) => {
					if (this.target) {
						return this.target.on[prop];
					} else {
						return (...args) => {
							this.onQueue.push({
								method: prop,
								args,
							});
						};
					}
				},
			});
			this.proxiedTarget = new Proxy({}, {
				get: (_target, prop) => {
					if (this.target) {
						return this.target[prop];
					} else if (prop === 'on') {
						return this.proxiedOn;
					} else if (Object.keys(this.fallbacks).includes(prop)) {
						return (...args) => {
							this.targetQueue.push({
								method: prop,
								args,
								resolve: () => {},
							});
							return this.fallbacks[prop](...args);
						};
					} else {
						return (...args) => {
							return new Promise(resolve => {
								this.targetQueue.push({
									method: prop,
									args,
									resolve,
								});
							});
						};
					}
				},
			});
		}
		async setRealTarget(target) {
			this.target = target;
			for (const item of this.onQueue) {
				this.target.on[item.method](...item.args);
			}
			for (const item of this.targetQueue) {
				item.resolve(await this.target[item.method](...item.args));
			}
		}
	}

	function setupDevtoolsPlugin(pluginDescriptor, setupFn) {
		const descriptor = pluginDescriptor;
		const target = getTarget();
		const hook = getDevtoolsGlobalHook();
		const enableProxy = isProxyAvailable && descriptor.enableEarlyProxy;
		if (hook && (target.__VUE_DEVTOOLS_PLUGIN_API_AVAILABLE__ || !enableProxy)) {
			hook.emit(HOOK_SETUP, pluginDescriptor, setupFn);
		} else {
			const proxy = enableProxy ? new ApiProxy(descriptor, hook) : null;
			const list = target.__VUE_DEVTOOLS_PLUGINS__ = target.__VUE_DEVTOOLS_PLUGINS__ || [];
			list.push({
				pluginDescriptor: descriptor,
				setupFn,
				proxy,
			});
			if (proxy)
				setupFn(proxy.proxiedTarget);
		}
	}

	function formatRouteLocation(routeLocation, tooltip) {
		const copy = assign({}, routeLocation, {
			// remove variables that can contain vue instances
			matched: routeLocation.matched.map(matched => omit(matched, ['instances', 'children',
				'aliasOf'
			])),
		});
		return {
			_custom: {
				type: null,
				readOnly: true,
				display: routeLocation.fullPath,
				tooltip,
				value: copy,
			},
		};
	}

	function formatDisplay(display) {
		return {
			_custom: {
				display,
			},
		};
	}
	// to support multiple router instances
	let routerId = 0;

	function addDevtools(app, router, matcher) {
		// Take over router.beforeEach and afterEach
		// make sure we are not registering the devtool twice
		if (router.__hasDevtools)
			return;
		router.__hasDevtools = true;
		// increment to support multiple router instances
		const id = routerId++;
		setupDevtoolsPlugin({
			id: 'org.vuejs.router' + (id ? '.' + id : ''),
			label: 'Vue Router',
			packageName: 'vue-router',
			homepage: 'https://router.vuejs.org',
			logo: 'https://router.vuejs.org/logo.png',
			componentStateTypes: ['Routing'],
			app,
		}, api => {
			// display state added by the router
			api.on.inspectComponent((payload, ctx) => {
				if (payload.instanceData) {
					payload.instanceData.state.push({
						type: 'Routing',
						key: '$route',
						editable: false,
						value: formatRouteLocation(router.currentRoute.value,
							'Current Route'),
					});
				}
			});
			// mark router-link as active and display tags on router views
			api.on.visitComponentTree(({
				treeNode: node,
				componentInstance
			}) => {
				if (componentInstance.__vrv_devtools) {
					const info = componentInstance.__vrv_devtools;
					node.tags.push({
						label: (info.name ? `${info.name.toString()}: ` : '') + info
							.path,
						textColor: 0,
						tooltip: 'This component is rendered by &lt;router-view&gt;',
						backgroundColor: PINK_500,
					});
				}
				// if multiple useLink are used
				if (Array.isArray(componentInstance.__vrl_devtools)) {
					componentInstance.__devtoolsApi = api;
					componentInstance.__vrl_devtools.forEach(devtoolsData => {
						let backgroundColor = ORANGE_400;
						let tooltip = '';
						if (devtoolsData.isExactActive) {
							backgroundColor = LIME_500;
							tooltip = 'This is exactly active';
						} else if (devtoolsData.isActive) {
							backgroundColor = BLUE_600;
							tooltip = 'This link is active';
						}
						node.tags.push({
							label: devtoolsData.route.path,
							textColor: 0,
							tooltip,
							backgroundColor,
						});
					});
				}
			});
			vue.watch(router.currentRoute, () => {
				// refresh active state
				refreshRoutesView();
				api.notifyComponentUpdate();
				api.sendInspectorTree(routerInspectorId);
				api.sendInspectorState(routerInspectorId);
			});
			const navigationsLayerId = 'router:navigations:' + id;
			api.addTimelineLayer({
				id: navigationsLayerId,
				label: `Router${id ? ' ' + id : ''} Navigations`,
				color: 0x40a8c4,
			});
			// const errorsLayerId = 'router:errors'
			// api.addTimelineLayer({
			//   id: errorsLayerId,
			//   label: 'Router Errors',
			//   color: 0xea5455,
			// })
			router.onError((error, to) => {
				api.addTimelineEvent({
					layerId: navigationsLayerId,
					event: {
						title: 'Error during Navigation',
						subtitle: to.fullPath,
						logType: 'error',
						time: api.now(),
						data: {
							error
						},
						groupId: to.meta.__navigationId,
					},
				});
			});
			// attached to `meta` and used to group events
			let navigationId = 0;
			router.beforeEach((to, from) => {
				const data = {
					guard: formatDisplay('beforeEach'),
					from: formatRouteLocation(from,
						'Current Location during this navigation'),
					to: formatRouteLocation(to, 'Target location'),
				};
				// Used to group navigations together, hide from devtools
				Object.defineProperty(to.meta, '__navigationId', {
					value: navigationId++,
				});
				api.addTimelineEvent({
					layerId: navigationsLayerId,
					event: {
						time: api.now(),
						title: 'Start of navigation',
						subtitle: to.fullPath,
						data,
						groupId: to.meta.__navigationId,
					},
				});
			});
			router.afterEach((to, from, failure) => {
				const data = {
					guard: formatDisplay('afterEach'),
				};
				if (failure) {
					data.failure = {
						_custom: {
							type: Error,
							readOnly: true,
							display: failure ? failure.message : '',
							tooltip: 'Navigation Failure',
							value: failure,
						},
					};
					data.status = formatDisplay('❌');
				} else {
					data.status = formatDisplay('✅');
				}
				// we set here to have the right order
				data.from = formatRouteLocation(from,
				'Current Location during this navigation');
				data.to = formatRouteLocation(to, 'Target location');
				api.addTimelineEvent({
					layerId: navigationsLayerId,
					event: {
						title: 'End of navigation',
						subtitle: to.fullPath,
						time: api.now(),
						data,
						logType: failure ? 'warning' : 'default',
						groupId: to.meta.__navigationId,
					},
				});
			});
			/**
			 * Inspector of Existing routes
			 */
			const routerInspectorId = 'router-inspector:' + id;
			api.addInspector({
				id: routerInspectorId,
				label: 'Routes' + (id ? ' ' + id : ''),
				icon: 'book',
				treeFilterPlaceholder: 'Search routes',
			});

			function refreshRoutesView() {
				// the routes view isn't active
				if (!activeRoutesPayload)
					return;
				const payload = activeRoutesPayload;
				// children routes will appear as nested
				let routes = matcher.getRoutes().filter(route => !route.parent);
				// reset match state to false
				routes.forEach(resetMatchStateOnRouteRecord);
				// apply a match state if there is a payload
				if (payload.filter) {
					routes = routes.filter(route =>
						// save matches state based on the payload
						isRouteMatching(route, payload.filter.toLowerCase()));
				}
				// mark active routes
				routes.forEach(route => markRouteRecordActive(route, router.currentRoute.value));
				payload.rootNodes = routes.map(formatRouteRecordForInspector);
			}
			let activeRoutesPayload;
			api.on.getInspectorTree(payload => {
				activeRoutesPayload = payload;
				if (payload.app === app && payload.inspectorId === routerInspectorId) {
					refreshRoutesView();
				}
			});
			/**
			 * Display information about the currently selected route record
			 */
			api.on.getInspectorState(payload => {
				if (payload.app === app && payload.inspectorId === routerInspectorId) {
					const routes = matcher.getRoutes();
					const route = routes.find(route => route.record.__vd_id === payload.nodeId);
					if (route) {
						payload.state = {
							options: formatRouteRecordMatcherForStateInspector(route),
						};
					}
				}
			});
			api.sendInspectorTree(routerInspectorId);
			api.sendInspectorState(routerInspectorId);
		});
	}

	function modifierForKey(key) {
		if (key.optional) {
			return key.repeatable ? '*' : '?';
		} else {
			return key.repeatable ? '+' : '';
		}
	}

	function formatRouteRecordMatcherForStateInspector(route) {
		const {
			record
		} = route;
		const fields = [{
			editable: false,
			key: 'path',
			value: record.path
		}, ];
		if (record.name != null) {
			fields.push({
				editable: false,
				key: 'name',
				value: record.name,
			});
		}
		fields.push({
			editable: false,
			key: 'regexp',
			value: route.re
		});
		if (route.keys.length) {
			fields.push({
				editable: false,
				key: 'keys',
				value: {
					_custom: {
						type: null,
						readOnly: true,
						display: route.keys
							.map(key => `${key.name}${modifierForKey(key)}`)
							.join(' '),
						tooltip: 'Param keys',
						value: route.keys,
					},
				},
			});
		}
		if (record.redirect != null) {
			fields.push({
				editable: false,
				key: 'redirect',
				value: record.redirect,
			});
		}
		if (route.alias.length) {
			fields.push({
				editable: false,
				key: 'aliases',
				value: route.alias.map(alias => alias.record.path),
			});
		}
		fields.push({
			key: 'score',
			editable: false,
			value: {
				_custom: {
					type: null,
					readOnly: true,
					display: route.score.map(score => score.join(', ')).join(' | '),
					tooltip: 'Score used to sort routes',
					value: route.score,
				},
			},
		});
		return fields;
	}
	/**
	 * Extracted from tailwind palette
	 */
	const PINK_500 = 0xec4899;
	const BLUE_600 = 0x2563eb;
	const LIME_500 = 0x84cc16;
	const CYAN_400 = 0x22d3ee;
	const ORANGE_400 = 0xfb923c;
	// const GRAY_100 = 0xf4f4f5
	const DARK = 0x666666;

	function formatRouteRecordForInspector(route) {
		const tags = [];
		const {
			record
		} = route;
		if (record.name != null) {
			tags.push({
				label: String(record.name),
				textColor: 0,
				backgroundColor: CYAN_400,
			});
		}
		if (record.aliasOf) {
			tags.push({
				label: 'alias',
				textColor: 0,
				backgroundColor: ORANGE_400,
			});
		}
		if (route.__vd_match) {
			tags.push({
				label: 'matches',
				textColor: 0,
				backgroundColor: PINK_500,
			});
		}
		if (route.__vd_exactActive) {
			tags.push({
				label: 'exact',
				textColor: 0,
				backgroundColor: LIME_500,
			});
		}
		if (route.__vd_active) {
			tags.push({
				label: 'active',
				textColor: 0,
				backgroundColor: BLUE_600,
			});
		}
		if (record.redirect) {
			tags.push({
				label: 'redirect: ' +
					(typeof record.redirect === 'string' ? record.redirect : 'Object'),
				textColor: 0xffffff,
				backgroundColor: DARK,
			});
		}
		// add an id to be able to select it. Using the `path` is not possible because
		// empty path children would collide with their parents
		let id = record.__vd_id;
		if (id == null) {
			id = String(routeRecordId++);
			record.__vd_id = id;
		}
		return {
			id,
			label: record.path,
			tags,
			children: route.children.map(formatRouteRecordForInspector),
		};
	}
	//  incremental id for route records and inspector state
	let routeRecordId = 0;
	const EXTRACT_REGEXP_RE = /^\/(.*)\/([a-z]*)$/;

	function markRouteRecordActive(route, currentRoute) {
		// no route will be active if matched is empty
		// reset the matching state
		const isExactActive = currentRoute.matched.length &&
			isSameRouteRecord(currentRoute.matched[currentRoute.matched.length - 1], route.record);
		route.__vd_exactActive = route.__vd_active = isExactActive;
		if (!isExactActive) {
			route.__vd_active = currentRoute.matched.some(match => isSameRouteRecord(match, route.record));
		}
		route.children.forEach(childRoute => markRouteRecordActive(childRoute, currentRoute));
	}

	function resetMatchStateOnRouteRecord(route) {
		route.__vd_match = false;
		route.children.forEach(resetMatchStateOnRouteRecord);
	}

	function isRouteMatching(route, filter) {
		const found = String(route.re).match(EXTRACT_REGEXP_RE);
		route.__vd_match = false;
		if (!found || found.length < 3) {
			return false;
		}
		// use a regexp without $ at the end to match nested routes better
		const nonEndingRE = new RegExp(found[1].replace(/\$$/, ''), found[2]);
		if (nonEndingRE.test(filter)) {
			// mark children as matches
			route.children.forEach(child => isRouteMatching(child, filter));
			// exception case: `/`
			if (route.record.path !== '/' || filter === '/') {
				route.__vd_match = route.re.test(filter);
				return true;
			}
			// hide the / route
			return false;
		}
		const path = route.record.path.toLowerCase();
		const decodedPath = decode(path);
		// also allow partial matching on the path
		if (!filter.startsWith('/') &&
			(decodedPath.includes(filter) || path.includes(filter)))
			return true;
		if (decodedPath.startsWith(filter) || path.startsWith(filter))
			return true;
		if (route.record.name && String(route.record.name).includes(filter))
			return true;
		return route.children.some(child => isRouteMatching(child, filter));
	}

	function omit(obj, keys) {
		const ret = {};
		for (const key in obj) {
			if (!keys.includes(key)) {
				// @ts-expect-error
				ret[key] = obj[key];
			}
		}
		return ret;
	}

	/**
	 * Creates a Router instance that can be used by a Vue app.
	 *
	 * @param options - {@link RouterOptions}
	 */
	function createRouter(options) {
		const matcher = createRouterMatcher(options.routes, options);
		const parseQuery$1 = options.parseQuery || parseQuery;
		const stringifyQuery$1 = options.stringifyQuery || stringifyQuery;
		const routerHistory = options.history;
		if (!routerHistory)
			throw new Error('Provide the "history" option when calling "createRouter()":' +
				' https://next.router.vuejs.org/api/#history.');
		const beforeGuards = useCallbacks();
		const beforeResolveGuards = useCallbacks();
		const afterGuards = useCallbacks();
		const currentRoute = vue.shallowRef(START_LOCATION_NORMALIZED);
		let pendingLocation = START_LOCATION_NORMALIZED;
		// leave the scrollRestoration if no scrollBehavior is provided
		if (isBrowser && options.scrollBehavior && 'scrollRestoration' in history) {
			history.scrollRestoration = 'manual';
		}
		const normalizeParams = applyToParams.bind(null, paramValue => '' + paramValue);
		const encodeParams = applyToParams.bind(null, encodeParam);
		const decodeParams =
			// @ts-expect-error: intentionally avoid the type check
			applyToParams.bind(null, decode);

		function addRoute(parentOrRoute, route) {
			let parent;
			let record;
			if (isRouteName(parentOrRoute)) {
				parent = matcher.getRecordMatcher(parentOrRoute);
				record = route;
			} else {
				record = parentOrRoute;
			}
			return matcher.addRoute(record, parent);
		}

		function removeRoute(name) {
			const recordMatcher = matcher.getRecordMatcher(name);
			if (recordMatcher) {
				matcher.removeRoute(recordMatcher);
			} else {
				warn(`Cannot remove non-existent route "${String(name)}"`);
			}
		}

		function getRoutes() {
			return matcher.getRoutes().map(routeMatcher => routeMatcher.record);
		}

		function hasRoute(name) {
			return !!matcher.getRecordMatcher(name);
		}

		function resolve(rawLocation, currentLocation) {
			// const objectLocation = routerLocationAsObject(rawLocation)
			// we create a copy to modify it later
			currentLocation = assign({}, currentLocation || currentRoute.value);
			if (typeof rawLocation === 'string') {
				const locationNormalized = parseURL(parseQuery$1, rawLocation, currentLocation.path);
				const matchedRoute = matcher.resolve({
					path: locationNormalized.path
				}, currentLocation);
				const href = routerHistory.createHref(locationNormalized.fullPath); {
					if (href.startsWith('//'))
						warn(
							`Location "${rawLocation}" resolved to "${href}". A resolved location cannot start with multiple slashes.`
							);
					else if (!matchedRoute.matched.length) {
						warn(`No match found for location with path "${rawLocation}"`);
					}
				}
				// locationNormalized is always a new object
				return assign(locationNormalized, matchedRoute, {
					params: decodeParams(matchedRoute.params),
					hash: decode(locationNormalized.hash),
					redirectedFrom: undefined,
					href,
				});
			}
			let matcherLocation;
			// path could be relative in object as well
			if ('path' in rawLocation) {
				if ('params' in rawLocation &&
					!('name' in rawLocation) &&
					// @ts-expect-error: the type is never
					Object.keys(rawLocation.params).length) {
					warn(
						`Path "${
                // @ts-expect-error: the type is never
                rawLocation.path}" was passed with params but they will be ignored. Use a named route alongside params instead.`);
				}
				matcherLocation = assign({}, rawLocation, {
					path: parseURL(parseQuery$1, rawLocation.path, currentLocation.path).path,
				});
			} else {
				// remove any nullish param
				const targetParams = assign({}, rawLocation.params);
				for (const key in targetParams) {
					if (targetParams[key] == null) {
						delete targetParams[key];
					}
				}
				// pass encoded values to the matcher so it can produce encoded path and fullPath
				matcherLocation = assign({}, rawLocation, {
					params: encodeParams(rawLocation.params),
				});
				// current location params are decoded, we need to encode them in case the
				// matcher merges the params
				currentLocation.params = encodeParams(currentLocation.params);
			}
			const matchedRoute = matcher.resolve(matcherLocation, currentLocation);
			const hash = rawLocation.hash || '';
			if (hash && !hash.startsWith('#')) {
				warn(
					`A \`hash\` should always start with the character "#". Replace "${hash}" with "#${hash}".`);
			}
			// decoding them) the matcher might have merged current location params so
			// we need to run the decoding again
			matchedRoute.params = normalizeParams(decodeParams(matchedRoute.params));
			const fullPath = stringifyURL(stringifyQuery$1, assign({}, rawLocation, {
				hash: encodeHash(hash),
				path: matchedRoute.path,
			}));
			const href = routerHistory.createHref(fullPath); {
				if (href.startsWith('//')) {
					warn(
						`Location "${rawLocation}" resolved to "${href}". A resolved location cannot start with multiple slashes.`);
				} else if (!matchedRoute.matched.length) {
					warn(
						`No match found for location with path "${'path' in rawLocation ? rawLocation.path : rawLocation}"`);
				}
			}
			return assign({
				fullPath,
				// keep the hash encoded so fullPath is effectively path + encodedQuery +
				// hash
				hash,
				query:
					// if the user is using a custom query lib like qs, we might have
					// nested objects, so we keep the query as is, meaning it can contain
					// numbers at `$route.query`, but at the point, the user will have to
					// use their own type anyway.
					// https://github.com/vuejs/router/issues/328#issuecomment-649481567
					stringifyQuery$1 === stringifyQuery ?
					normalizeQuery(rawLocation.query) :
					(rawLocation.query || {}),
			}, matchedRoute, {
				redirectedFrom: undefined,
				href,
			});
		}

		function locationAsObject(to) {
			return typeof to === 'string' ?
				parseURL(parseQuery$1, to, currentRoute.value.path) :
				assign({}, to);
		}

		function checkCanceledNavigation(to, from) {
			if (pendingLocation !== to) {
				return createRouterError(8 /* NAVIGATION_CANCELLED */ , {
					from,
					to,
				});
			}
		}

		function push(to) {
			return pushWithRedirect(to);
		}

		function replace(to) {
			return push(assign(locationAsObject(to), {
				replace: true
			}));
		}

		function handleRedirectRecord(to) {
			const lastMatched = to.matched[to.matched.length - 1];
			if (lastMatched && lastMatched.redirect) {
				const {
					redirect
				} = lastMatched;
				let newTargetLocation = typeof redirect === 'function' ? redirect(to) : redirect;
				if (typeof newTargetLocation === 'string') {
					newTargetLocation =
						newTargetLocation.includes('?') || newTargetLocation.includes('#') ?
						(newTargetLocation = locationAsObject(newTargetLocation)) : // force empty params
						{
							path: newTargetLocation
						};
					// @ts-expect-error: force empty params when a string is passed to let
					// the router parse them again
					newTargetLocation.params = {};
				}
				if (!('path' in newTargetLocation) &&
					!('name' in newTargetLocation)) {
					warn(
						`Invalid redirect found:\n${JSON.stringify(newTargetLocation, null, 2)}\n when navigating to "${to.fullPath}". A redirect must contain a name or path. This will break in production.`);
					throw new Error('Invalid redirect');
				}
				return assign({
					query: to.query,
					hash: to.hash,
					params: to.params,
				}, newTargetLocation);
			}
		}

		function pushWithRedirect(to, redirectedFrom) {
			const targetLocation = (pendingLocation = resolve(to));
			const from = currentRoute.value;
			const data = to.state;
			const force = to.force;
			// to could be a string where `replace` is a function
			const replace = to.replace === true;
			const shouldRedirect = handleRedirectRecord(targetLocation);
			if (shouldRedirect)
				return pushWithRedirect(assign(locationAsObject(shouldRedirect), {
						state: data,
						force,
						replace,
					}),
					// keep original redirectedFrom if it exists
					redirectedFrom || targetLocation);
			// if it was a redirect we already called `pushWithRedirect` above
			const toLocation = targetLocation;
			toLocation.redirectedFrom = redirectedFrom;
			let failure;
			if (!force && isSameRouteLocation(stringifyQuery$1, from, targetLocation)) {
				failure = createRouterError(16 /* NAVIGATION_DUPLICATED */ , {
					to: toLocation,
					from
				});
				// trigger scroll to allow scrolling to the same anchor
				handleScroll(from, from,
					// this is a push, the only way for it to be triggered from a
					// history.listen is with a redirect, which makes it become a push
					true,
					// This cannot be the first navigation because the initial location
					// cannot be manually navigated to
					false);
			}
			return (failure ? Promise.resolve(failure) : navigate(toLocation, from))
				.catch((error) => isNavigationFailure(
					error) ? // navigation redirects still mark the router as ready
					isNavigationFailure(error, 2 /* NAVIGATION_GUARD_REDIRECT */ ) ?
					error :
					markAsReady(error) // also returns the error
					: // reject any unknown error
					triggerError(error, toLocation, from))
				.then((failure) => {
					if (failure) {
						if (isNavigationFailure(failure, 2 /* NAVIGATION_GUARD_REDIRECT */ )) {
							if ( // we are redirecting to the same location we were already at
								isSameRouteLocation(stringifyQuery$1, resolve(failure.to), toLocation) &&
								// and we have done it a couple of times
								redirectedFrom &&
								// @ts-expect-error: added only in dev
								(redirectedFrom._count = redirectedFrom._count ? // @ts-expect-error
									redirectedFrom._count + 1 :
									1) > 10) {
								warn(
									`Detected an infinite redirection in a navigation guard when going from "${from.fullPath}" to "${toLocation.fullPath}". Aborting to avoid a Stack Overflow. This will break in production if not fixed.`);
								return Promise.reject(new Error('Infinite redirect in navigation guard'));
							}
							return pushWithRedirect(
								// keep options
								assign(locationAsObject(failure.to), {
									state: data,
									force,
									replace,
								}),
								// preserve the original redirectedFrom if any
								redirectedFrom || toLocation);
						}
					} else {
						// if we fail we don't finalize the navigation
						failure = finalizeNavigation(toLocation, from, true, replace, data);
					}
					triggerAfterEach(toLocation, from, failure);
					return failure;
				});
		}
		/**
		 * Helper to reject and skip all navigation guards if a new navigation happened
		 * @param to
		 * @param from
		 */
		function checkCanceledNavigationAndReject(to, from) {
			const error = checkCanceledNavigation(to, from);
			return error ? Promise.reject(error) : Promise.resolve();
		}
		// TODO: refactor the whole before guards by internally using router.beforeEach
		function navigate(to, from) {
			let guards;
			const [leavingRecords, updatingRecords, enteringRecords] = extractChangingRecords(to, from);
			// all components here have been resolved once because we are leaving
			guards = extractComponentsGuards(leavingRecords.reverse(), 'beforeRouteLeave', to, from);
			// leavingRecords is already reversed
			for (const record of leavingRecords) {
				record.leaveGuards.forEach(guard => {
					guards.push(guardToPromiseFn(guard, to, from));
				});
			}
			const canceledNavigationCheck = checkCanceledNavigationAndReject.bind(null, to, from);
			guards.push(canceledNavigationCheck);
			// run the queue of per route beforeRouteLeave guards
			return (runGuardQueue(guards)
				.then(() => {
					// check global guards beforeEach
					guards = [];
					for (const guard of beforeGuards.list()) {
						guards.push(guardToPromiseFn(guard, to, from));
					}
					guards.push(canceledNavigationCheck);
					return runGuardQueue(guards);
				})
				.then(() => {
					// check in components beforeRouteUpdate
					guards = extractComponentsGuards(updatingRecords, 'beforeRouteUpdate', to, from);
					for (const record of updatingRecords) {
						record.updateGuards.forEach(guard => {
							guards.push(guardToPromiseFn(guard, to, from));
						});
					}
					guards.push(canceledNavigationCheck);
					// run the queue of per route beforeEnter guards
					return runGuardQueue(guards);
				})
				.then(() => {
					// check the route beforeEnter
					guards = [];
					for (const record of to.matched) {
						// do not trigger beforeEnter on reused views
						if (record.beforeEnter && !from.matched.includes(record)) {
							if (Array.isArray(record.beforeEnter)) {
								for (const beforeEnter of record.beforeEnter)
									guards.push(guardToPromiseFn(beforeEnter, to, from));
							} else {
								guards.push(guardToPromiseFn(record.beforeEnter, to, from));
							}
						}
					}
					guards.push(canceledNavigationCheck);
					// run the queue of per route beforeEnter guards
					return runGuardQueue(guards);
				})
				.then(() => {
					// NOTE: at this point to.matched is normalized and does not contain any () => Promise<Component>
					// clear existing enterCallbacks, these are added by extractComponentsGuards
					to.matched.forEach(record => (record.enterCallbacks = {}));
					// check in-component beforeRouteEnter
					guards = extractComponentsGuards(enteringRecords, 'beforeRouteEnter', to, from);
					guards.push(canceledNavigationCheck);
					// run the queue of per route beforeEnter guards
					return runGuardQueue(guards);
				})
				.then(() => {
					// check global guards beforeResolve
					guards = [];
					for (const guard of beforeResolveGuards.list()) {
						guards.push(guardToPromiseFn(guard, to, from));
					}
					guards.push(canceledNavigationCheck);
					return runGuardQueue(guards);
				})
				// catch any navigation canceled
				.catch(err => isNavigationFailure(err, 8 /* NAVIGATION_CANCELLED */ ) ?
					err :
					Promise.reject(err)));
		}

		function triggerAfterEach(to, from, failure) {
			// navigation is confirmed, call afterGuards
			// TODO: wrap with error handlers
			for (const guard of afterGuards.list())
				guard(to, from, failure);
		}
		/**
		 * - Cleans up any navigation guards
		 * - Changes the url if necessary
		 * - Calls the scrollBehavior
		 */
		function finalizeNavigation(toLocation, from, isPush, replace, data) {
			// a more recent navigation took place
			const error = checkCanceledNavigation(toLocation, from);
			if (error)
				return error;
			// only consider as push if it's not the first navigation
			const isFirstNavigation = from === START_LOCATION_NORMALIZED;
			const state = !isBrowser ? {} : history.state;
			// change URL only if the user did a push/replace and if it's not the initial navigation because
			// it's just reflecting the url
			if (isPush) {
				// on the initial navigation, we want to reuse the scroll position from
				// history state if it exists
				if (replace || isFirstNavigation)
					routerHistory.replace(toLocation.fullPath, assign({
						scroll: isFirstNavigation && state && state.scroll,
					}, data));
				else
					routerHistory.push(toLocation.fullPath, data);
			}
			// accept current navigation
			currentRoute.value = toLocation;
			handleScroll(toLocation, from, isPush, isFirstNavigation);
			markAsReady();
		}
		let removeHistoryListener;
		// attach listener to history to trigger navigations
		function setupListeners() {
			// avoid setting up listeners twice due to an invalid first navigation
			if (removeHistoryListener)
				return;
			removeHistoryListener = routerHistory.listen((to, _from, info) => {
				// cannot be a redirect route because it was in history
				const toLocation = resolve(to);
				// due to dynamic routing, and to hash history with manual navigation
				// (manually changing the url or calling history.hash = '#/somewhere'),
				// there could be a redirect record in history
				const shouldRedirect = handleRedirectRecord(toLocation);
				if (shouldRedirect) {
					pushWithRedirect(assign(shouldRedirect, {
						replace: true
					}), toLocation).catch(noop);
					return;
				}
				pendingLocation = toLocation;
				const from = currentRoute.value;
				// TODO: should be moved to web history?
				if (isBrowser) {
					saveScrollPosition(getScrollKey(from.fullPath, info.delta),
				computeScrollPosition());
				}
				navigate(toLocation, from)
					.catch((error) => {
						if (isNavigationFailure(error, 4 /* NAVIGATION_ABORTED */ |
								8 /* NAVIGATION_CANCELLED */ )) {
							return error;
						}
						if (isNavigationFailure(error, 2 /* NAVIGATION_GUARD_REDIRECT */ )) {
							// Here we could call if (info.delta) routerHistory.go(-info.delta,
							// false) but this is bug prone as we have no way to wait the
							// navigation to be finished before calling pushWithRedirect. Using
							// a setTimeout of 16ms seems to work but there is not guarantee for
							// it to work on every browser. So Instead we do not restore the
							// history entry and trigger a new navigation as requested by the
							// navigation guard.
							// the error is already handled by router.push we just want to avoid
							// logging the error
							pushWithRedirect(error.to, toLocation
									// avoid an uncaught rejection, let push call triggerError
								)
								.then(failure => {
									// manual change in hash history #916 ending up in the URL not
									// changing but it was changed by the manual url change, so we
									// need to manually change it ourselves
									if (isNavigationFailure(failure,
											4 /* NAVIGATION_ABORTED */ |
											16 /* NAVIGATION_DUPLICATED */ ) &&
										!info.delta &&
										info.type === NavigationType.pop) {
										routerHistory.go(-1, false);
									}
								})
								.catch(noop);
							// avoid the then branch
							return Promise.reject();
						}
						// do not restore history on unknown direction
						if (info.delta)
							routerHistory.go(-info.delta, false);
						// unrecognized error, transfer to the global handler
						return triggerError(error, toLocation, from);
					})
					.then((failure) => {
						failure =
							failure ||
							finalizeNavigation(
								// after navigation, all matched components are resolved
								toLocation, from, false);
						// revert the navigation
						if (failure) {
							if (info.delta) {
								routerHistory.go(-info.delta, false);
							} else if (info.type === NavigationType.pop &&
								isNavigationFailure(failure, 4 /* NAVIGATION_ABORTED */ |
									16 /* NAVIGATION_DUPLICATED */ )) {
								// manual change in hash history #916
								// it's like a push but lacks the information of the direction
								routerHistory.go(-1, false);
							}
						}
						triggerAfterEach(toLocation, from, failure);
					})
					.catch(noop);
			});
		}
		// Initialization and Errors
		let readyHandlers = useCallbacks();
		let errorHandlers = useCallbacks();
		let ready;
		/**
		 * Trigger errorHandlers added via onError and throws the error as well
		 *
		 * @param error - error to throw
		 * @param to - location we were navigating to when the error happened
		 * @param from - location we were navigating from when the error happened
		 * @returns the error as a rejected promise
		 */
		function triggerError(error, to, from) {
			markAsReady(error);
			const list = errorHandlers.list();
			if (list.length) {
				list.forEach(handler => handler(error, to, from));
			} else {
				{
					warn('uncaught error during route navigation:');
				}
				console.error(error);
			}
			return Promise.reject(error);
		}

		function isReady() {
			if (ready && currentRoute.value !== START_LOCATION_NORMALIZED)
				return Promise.resolve();
			return new Promise((resolve, reject) => {
				readyHandlers.add([resolve, reject]);
			});
		}

		function markAsReady(err) {
			if (!ready) {
				// still not ready if an error happened
				ready = !err;
				setupListeners();
				readyHandlers
					.list()
					.forEach(([resolve, reject]) => (err ? reject(err) : resolve()));
				readyHandlers.reset();
			}
			return err;
		}
		// Scroll behavior
		function handleScroll(to, from, isPush, isFirstNavigation) {
			const {
				scrollBehavior
			} = options;
			if (!isBrowser || !scrollBehavior)
				return Promise.resolve();
			const scrollPosition = (!isPush && getSavedScrollPosition(getScrollKey(to.fullPath, 0))) ||
				((isFirstNavigation || !isPush) &&
					history.state &&
					history.state.scroll) ||
				null;
			return vue.nextTick()
				.then(() => scrollBehavior(to, from, scrollPosition))
				.then(position => position && scrollToPosition(position))
				.catch(err => triggerError(err, to, from));
		}
		const go = (delta) => routerHistory.go(delta);
		let started;
		const installedApps = new Set();
		const router = {
			currentRoute,
			addRoute,
			removeRoute,
			hasRoute,
			getRoutes,
			resolve,
			options,
			push,
			replace,
			go,
			back: () => go(-1),
			forward: () => go(1),
			beforeEach: beforeGuards.add,
			beforeResolve: beforeResolveGuards.add,
			afterEach: afterGuards.add,
			onError: errorHandlers.add,
			isReady,
			install(app) {
				const router = this;
				app.component('RouterLink', RouterLink);
				app.component('RouterView', RouterView);
				app.config.globalProperties.$router = router;
				Object.defineProperty(app.config.globalProperties, '$route', {
					enumerable: true,
					get: () => vue.unref(currentRoute),
				});
				// this initial navigation is only necessary on client, on server it doesn't
				// make sense because it will create an extra unnecessary navigation and could
				// lead to problems
				if (isBrowser &&
					// used for the initial navigation client side to avoid pushing
					// multiple times when the router is used in multiple apps
					!started &&
					currentRoute.value === START_LOCATION_NORMALIZED) {
					// see above
					started = true;
					push(routerHistory.location).catch(err => {
						warn('Unexpected error when starting the router:', err);
					});
				}
				const reactiveRoute = {};
				for (const key in START_LOCATION_NORMALIZED) {
					// @ts-expect-error: the key matches
					reactiveRoute[key] = vue.computed(() => currentRoute.value[key]);
				}
				app.provide(routerKey, router);
				app.provide(routeLocationKey, vue.reactive(reactiveRoute));
				app.provide(routerViewLocationKey, currentRoute);
				const unmountApp = app.unmount;
				installedApps.add(app);
				app.unmount = function() {
					installedApps.delete(app);
					// the router is not attached to an app anymore
					if (installedApps.size < 1) {
						// invalidate the current navigation
						pendingLocation = START_LOCATION_NORMALIZED;
						removeHistoryListener && removeHistoryListener();
						removeHistoryListener = null;
						currentRoute.value = START_LOCATION_NORMALIZED;
						started = false;
						ready = false;
					}
					unmountApp();
				};
				if (isBrowser) {
					addDevtools(app, router, matcher);
				}
			},
		};
		return router;
	}

	function runGuardQueue(guards) {
		return guards.reduce((promise, guard) => promise.then(() => guard()), Promise.resolve());
	}

	function extractChangingRecords(to, from) {
		const leavingRecords = [];
		const updatingRecords = [];
		const enteringRecords = [];
		const len = Math.max(from.matched.length, to.matched.length);
		for (let i = 0; i < len; i++) {
			const recordFrom = from.matched[i];
			if (recordFrom) {
				if (to.matched.find(record => isSameRouteRecord(record, recordFrom)))
					updatingRecords.push(recordFrom);
				else
					leavingRecords.push(recordFrom);
			}
			const recordTo = to.matched[i];
			if (recordTo) {
				// the type doesn't matter because we are comparing per reference
				if (!from.matched.find(record => isSameRouteRecord(record, recordTo))) {
					enteringRecords.push(recordTo);
				}
			}
		}
		return [leavingRecords, updatingRecords, enteringRecords];
	}

	/**
	 * Returns the router instance. Equivalent to using `$router` inside
	 * templates.
	 */
	function useRouter() {
		return vue.inject(routerKey);
	}
	/**
	 * Returns the current route location. Equivalent to using `$route` inside
	 * templates.
	 */
	function useRoute() {
		return vue.inject(routeLocationKey);
	}

	exports.RouterLink = RouterLink;
	exports.RouterView = RouterView;
	exports.START_LOCATION = START_LOCATION_NORMALIZED;
	exports.createMemoryHistory = createMemoryHistory;
	exports.createRouter = createRouter;
	exports.createRouterMatcher = createRouterMatcher;
	exports.createWebHashHistory = createWebHashHistory;
	exports.createWebHistory = createWebHistory;
	exports.isNavigationFailure = isNavigationFailure;
	exports.matchedRouteKey = matchedRouteKey;
	exports.onBeforeRouteLeave = onBeforeRouteLeave;
	exports.onBeforeRouteUpdate = onBeforeRouteUpdate;
	exports.parseQuery = parseQuery;
	exports.routeLocationKey = routeLocationKey;
	exports.routerKey = routerKey;
	exports.routerViewLocationKey = routerViewLocationKey;
	exports.stringifyQuery = stringifyQuery;
	exports.useLink = useLink;
	exports.useRoute = useRoute;
	exports.useRouter = useRouter;
	exports.viewDepthKey = viewDepthKey;

	Object.defineProperty(exports, '__esModule', {
		value: true
	});

	return exports;

})({}, Vue);
